TypeScript 2.0 的新特性
可識別空和未定義類型
TypeScript 有兩個特殊的類型,null 和 undefined, null 和 undefined 分別是它們的值。以前是不能顯式使用這些類型的,但現在 null 和 undefined 不管在什么類型檢查模式下都可以用作類型名稱。
以前的類型檢查器認為 null 和 undefined 可以賦值給任意變量。實際上, null 和 undefined 對 每一個 類型都是有效的值,任何類型都不可能明確定義不包含它們(因此不可能檢查到對它們的錯誤使用)。
strictNullChecks
strictNullChecks 參數用于新的嚴格空檢查模式。
在嚴格空檢查模式下, null 和 undefined 值都 不 屬于任何一個類型,它們只能賦值給自己這種類型或者 any (有一個例外, undefined 也可以賦值給 void )。因此,在常規類型檢查模式下 T 和 T | ndefined 被認為是等同的(因為 undefined 被看作 T 的子類型),但它們在嚴格類型檢查模式下是不同的類型,只有 T | undefined 類型允許出現 undefined 值。 T 和 T | null 也是這種情況。
示例
// 使用 --strictNullChecks 參數編譯
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1; // 正確
y = 1; // 正確
z = 1; // 正確
x = undefined; // 錯誤
y = undefined; // 正確
z = undefined; // 正確
x = null; // 錯誤
y = null; // 錯誤
z = null; // 正確
x = y; // 錯誤
x = z; // 錯誤
y = x; // 正確
y = z; // 錯誤
z = x; // 正確
z = y; // 正確
使用前賦值 檢查
在嚴格空檢查模式下,編譯器要求在任意可能先進到的代碼路徑前,不允許 undefined 值的變量引用都必須已經賦值。
示例
// 使用 --strictNullChecks 參數編譯
let x: number;
let y: number | null;
let z: number | undefined;
x; // 錯誤, 使用前未賦值
y; // 錯誤, 使用前未賦值
z; // 正確
x = 1;
y = null;
x; // 正確
y; // 正確
編譯器通過 基于控制流的類型分析 來檢查變量是否確實賦值. 稍后請進一步閱讀關于這個主題的細節。
可選參數和屬性
可選參數和屬性會自動將 undefined 加入它們的類型,哪怕在它們的類型申明中沒有特別指定 undefined 。比如下面兩個類型就是一致的。
// 使用 --strictNullChecks 參數編譯
type T1 = (x?: number) => string; // x 類型是 number | undefined
type T2 = (x?: number | undefined) => string; // x 類型是 number | undefined
非空(non-null)和非未定義(non-undefined)類型控制
如果一個對象是 null 或 undefined ,訪問它的屬性會引發編譯錯誤,對 null 或 undefined 進行函數調用也會引發編譯錯誤。不過類型檢查進行了擴展,支持對非空和非未定義類型進行檢查。
示例
// 使用 --strictNullChecks 參數編譯
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
f(x); // 正確, 這里 x 的類型是 number
}
else {
f(x); // 錯誤, 這里 x 是 number?
}
let a = x != null ? f(x) : ""; // a 的類型是字符串
let b = x && f(x); // b 的類型是 string | 0 | null | undefined
非空和非未定義類型檢查允許使用 == 、 != 、 === 、或者 !== 運算符來與 null 或者 undefined 進行比較,比如 x != null 或 x === undefined 。具體效果與 JavaScript 的語義一致。(例如,雙等號運算符檢查兩個值,不管哪一個是指定的,而三等號運算符只檢查指定的值)。
類型控制帶點的名稱
以前的類型控制只能檢查局部變量和參數。而現在它可以檢查“帶點的名稱〔譯者注:帶點指對象后面的點號運算符〕”,這類名稱由變量或參數以及后面的一個或多個屬性訪問組成。
示例
interface Options {
location?: {
x?: number;
y?: number;
};
}
function foo(options?: Options) {
if (options && options.location && options.location.x) {
const x = options.location.x; // x 的類型是 number
}
}</code></pre>
對帶點號和名稱的類型控制也會應用于用戶定義的類型控制功能,以及 typeof 和 instanceof 運算符,而且算不需要 --strictNullChecks 編譯參數。
對帶點號的名稱的類型控制會在對其中任意部分賦值后失敗。比如, x.y.z 的類型控制在 x 、 x.y 或 x.y.z 賦值后將失去效用。
表達式運算符
表達式運算符允許操作數類型包括 null 或/和 undefined ,但結果一定是非空非未定義的類型。
// 使用 --strictNullChecks 參數編譯
function sum(a: number | null, b: number | null) {
return a + b; // 結果類型是 number
}
&& 運算符的會根據在操作數的類型來添加 null 或/和 undefined 類型到右操作數的類型中。 || 則會從左操作數的類型中去掉 null 和 undefined 后,再用于推導結果類型。
// 使用 --strictNullChecks 參數編譯
interface Entity {
name: string;
}
let x: Entity | null;
let s = x && x.name; // s 類型是 string | null
let y = x || { name: "test" }; // y 類型是 Entity
類型擴展
在嚴格空檢查模型下, null 和 undefined 不會 擴展為 any 。
let z = null; // z 是 null
因為類型擴展,在常規類型檢查模式下 z 被推導為 any ,但在嚴格空類型檢查模式下對 z 的類型推導結果仍然是 null (并且,由于沒有指定類型, null 是 z 唯一可能的值)。
非空斷言運算符
新的 ! 后置運算符用于斷言它的操作數在檢查器不能推斷的情況下是非空非未定義的。舉例說明: x! 申明 x 的值不可能是 null 或 undefined 。與 <T>x 和 x as T 這兩種形式的類型申明相似,在生成 JavaScript 代碼時只是簡單地去掉了 ! 非空斷言運算符。
// 使用 --strictNullChecks 參數編譯
function validateEntity(e?: Entity) {
// 如果 e 是 null 或者無效的 Entity,拋出異常
}
function processEntity(e?: Entity) {
validateEntity(e);
let s = e!.name; // 斷言 e 非空,然后訪問它的 name 屬性
}</code></pre>
兼容性
這個新特性設置為可以在嚴格空檢查模式和常規類型檢查模式下都可使用。具體來說,在常規類型檢查模式下, null 和 undefined 類型會自動從聯合類型中剔除(因為它們已經是其它類型的子類型了), ! 非空斷言運算符允許存在但在常規類型檢查模式下不會有任何作用。這樣一來,使用了非空非未定義類型的申明文件就可以向后兼容,在常規類型檢查模型下使用。
在實際應用中,嚴格空檢查模式要求所有用于編譯的文件都是可識別空和未定義的。
基于控制流的類型分析
TypeScript 2.0 實現了基于控制流的類型分析,用于控制局部變量和參數。之前,用于類型控制的類型分析局限于 if 語句和 ?: 條件表達式,并不能用于賦值和控制流結構,如 return 和 break 語句。不管這個擁有聯合類型的變量和參數出現在什么地方,TypeScript 2.0 讓類型檢查分析貫穿于所有可能的流程,包括可能會產生極特別類型( 縮小范圍的類型 )的語句和表達式。
示例
function foo(x: string | number | boolean) {
if (typeof x === "string") {
x; // x 是 string 類型
x = 1;
x; // x 是 number 類型
}
x; // x 是 number | boolean 類型
}
function bar(x: string | number) {
if (typeof x === "number") {
return;
}
x; // x 是 string 類型
}</code></pre>
基于控制流的類型分析與 --strictNullChecks 模式極為相關,因為可空類型使用聯合類型來表示:
function test(x: string | null) {
if (x === null) {
return;
}
x; // 在函數后面的部分,x 是 string 類型
}
此外, --strictNullChecks 模式中,對不允許為 undefined 變量,基于控制流的類型分析還包含了 精確的賦值分析 。
function mumble(check: boolean) {
let x: number; // 這個類型不允許 undefined 值
x; // 錯誤, x 是 undefined
if (check) {
x = 1;
x; // 正確
}
x; // 錯誤, x 有可能是 undefined
x = 2;
x; // 正確
}
可推斷聯合類型
TypeScript 2.0 開始支持推斷(或可識別)聯合類型。特別指出,TS 編譯器現在支持限制聯合類型來對類型進行保護。這基于代碼中對標識屬性的檢查。這項功能也被擴展到 switch 語句。
Example
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
// 下面的 switch 語句中,每個 case 子句都限制了 s 的類型。
// 根據對標識屬性值的判斷,這使得既然不申明類型也可以根據推斷出來的類型訪問其它屬性。
switch (s.kind) {
case "square": return s.size s.size;
case "rectangle": return s.width s.height;
case "circle": return Math.PI s.radius s.radius;
}
}
function test1(s: Shape) {
if (s.kind === "square") {
s; // Square
}
else {
s; // Rectangle | Circle
}
}
function test2(s: Shape) {
if (s.kind === "square" || s.kind === "rectangle") {
return;
}
s; // Circle
}</code></pre>
標識屬性類型控制 來自于諸如 x.p == v 、 x.p === v 、 x.p != v 或 x.p !== v 這樣的表達式, p 和 v 是 string 字面類型〔譯者注:常量〕或者是一個 string 字面常量的聯合〔譯者注:比如 "type1" | "type2" | "type3" 這樣〕。 x 有一個 p 屬性,該屬性有一個可能的值 v ,標識屬性類型控制據此可以推斷 x 更精確的類型。
注意,目前我們僅支持標識屬性是 string 字面量類型的情況。我們計劃后面添加對布爾和數值字面量的支持。
never 類型
TypeScript 2.0 引入了新的基本類型 never 。
never 類型值表現為從未發生。 具體說來, never 用于函數的返回值申明,而這個函數實際沒有返回任何東西。在類型控制作用下, never 是不可能作為變量類型的。
never 類型有如下一些特征:
-
never 是所有類型的子類型,即可以賦值給任意類型。
-
沒有任何類型是 never 的子類型,所以不能賦值給 never ( never 自己除外)。
-
在沒有申明返回類型的函數表達式和箭頭函數中,如果沒有 return 語句,或者 return 語句返回的就是 結果為 never 的表達式,又或者函數結尾不可達(由控制流程分析判斷),則推斷函數的返回類型是 never 。
-
如果函數顯示申明了返回類型是 never ,所有 return 語句(如果有的話)必須返回結果為 never 的表達式,并且一定不可能到達函數結尾。
因為 never 是任何類型的子類型,所以一般不會在聯合類型中指定,并且如果函數中推導出來有其它類型返回, never 就會被忽略。
一些返回 never 的函數示例:
// 函數不能到達結束點,返回類型是 never
function error(message: string): never {
throw new Error(message);
}
// 推導返回類型是 never
function fail() {
return error("Something failed");
}
// 函數不能到達結束點,返回類型是 never
function infiniteLoop(): never {
while (true) {
}
}</code></pre>
一些使用返回 never 的函數的函數示例:
// 推導返回類型是 number
function move1(direction: "up" | "down") {
switch (direction) {
case "up":
return 1;
case "down":
return -1;
}
return error("Should never get here");
}
// 推導返回類型是 number
function move2(direction: "up" | "down") {
return direction === "up" ? 1 :
direction === "down" ? -1 :
error("Should never get here");
}
// 推導返回類型是 T
function check<T>(x: T | undefined) {
return x || error("Undefined value");
}</code></pre>
由于 never 可以賦值給任意類型,返回 never 的函數可以用于返回特定類型的回調函數:
function test(cb: () => string) {
let s = cb();
return s;
}
test(() => "hello");
test(() => fail());
test(() => { throw new Error(); })</code></pre>
只讀屬性和只讀索引
現在通過 readonly 修飾符,屬性或索引可以被申明為只讀的。
只讀屬性可以擁有初始化器,也可以定義它的類的構造函數中賦值,其它情況下都是不允許賦值的。
另外,有一些情況會產生 隱式的 只讀申明。
-
只有 get 訪問器沒有 set 訪問器的屬性被認為是只讀的。
-
枚舉類型的枚舉值是只讀的。
-
模塊對象中導出的 const 變量是只讀的。
-
import 語句中申明的實體是只讀的。
-
通過 ES2015 命名空間導入的實體是只讀的(例如: import * as foo from "foo" 中申明了 foo ,這時 foo.x 是只讀的。
Example
interface Point {
readonly x: number;
readonly y: number;
}
var p1: Point = { x: 10, y: 20 };
p1.x = 5; // 錯誤, p1.x 只讀
var p2 = { x: 1, y: 1 };
var p3: Point = p2; // 正確, p2 的只讀別名〔因為 Point 中的屬性定義為 readonly〕
p3.x = 5; // 錯誤, p3.x 只讀
p2.x = 5; // 正確, 同時也改變了 p3.x,因為 p3 是 p2 的(只讀)別名</code></pre>
class Foo {
readonly a = 1;
readonly b: string;
constructor() {
this.b = "hello"; // 構造函數中允許賦值
}
}
let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5; // 錯誤, 元素只讀
b.push(5); // 錯誤, 沒有 push 方法 (因為它是 array 的變種)
b.length = 3; // 錯誤, length 只讀
a = b; // 錯誤, 由于變種,部分方法已經不存在了
為函數指定 this
繼為類和接口指定 this 類型之后,函數和方法也可以申明它們所期望的 this 類型了。
默認情況下函數內部的 this 類型是 any 。從 TypeScript 2.0 開始,可以顯示的指代一個 this 參數。 this 參數不是一個真實的參數,而且它必須放在參數列表的第一位:
function f(this: void) {
// 確保在這個獨立的函數中不會用到 `this`
}
回調函數中的 this 參數
在功能庫中, this 參數可用于申明回調函數如何調用。
示例
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
this: void 表示 addClickListener 希望 onclick 是一個不需要 this 類型的函數。
現在如果需要使用 this 調用:
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// 天啊,這里用了 this,使用這個回調在運行時會導致巨大的錯誤
this.info = e.message;
};
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // 錯誤!
noImplicitThis
TypeScript 2.0 中加入了一個參數,標記所有函數中的 this 都沒有申明類型。
tsconfig.json 支持 Glob
支持 Glob 啦!!支持 Glob 是 最受歡迎特性中的一個 .
"include" 和 "exclude" 兩個參數支持使用 Glob 形式的文件模板。
示例
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "../../built/local/tsc.js",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
支持的 Glob 通配符包括:
-
* 匹配 0 個或更多字符(不包含目錄分隔符)
-
? 匹配 1 個字符(不包含目錄分隔符)
-
**/ 遞歸匹配任意子目錄
如果一段 Glob 模板只包含 * 或 .* ,則只有支持的文件擴展名被包含在內(如:默認的 .ts 、 .tsx 和 .d.ts ,如果 allowJs 設置為 true,則還有 .js 和 .jsx )。
如果 "files" 和 "include" 都未指定,編譯器默認包含所有包含目錄及子目錄下的 TypeScript( .ts 、 .d.ts 和 .tsx ) 文件,不過要排除 "exclude" 中指定的那些。如果 allowJs 設置為 true,JS 文件 ( .js and .jsx ) 也會包含在內。
如果指定了 "files" 或 "include" 屬性,編譯器會合并兩個屬性指定的文件。 "outDir" 選項指定目錄中的文件總是被排除在外,除非在 "files" 中特別指定( "exclude" 屬性中指定的也是這樣)。
"include" 包含的文件可以被 "exclude" 屬性過濾。然而 "files" 屬性指定的文件則不管 "exclude" 屬性的設置。 "exclude" 屬性未設置時,默認會排除 node_modules 、 bower_components 和 jspm_packages 目錄。
增強的模塊解決方案:基本URL,路徑映射,根目錄和跟蹤
TypeScript 2.0 提供了一系列的模塊解決方案工具來 通知 編譯器在哪里找到給定模塊的申明。
基礎URL
baseUrl 是 AMD 模塊加載系統常用的辦法,它描述了模塊在運行時應該從哪一個目錄“展開”。所有未指定相對路徑的導入都假定相對于 baseUrl 。
示例
{
"compilerOptions": {
"baseUrl": "./modules"
}
}
導入 "moduleA" 時會在 ./modules/moduleA 中查找。
import A from "moduleA";
路徑映射
有時候模塊并不直接放在 baseUrl 下。加載器使用一個映射配置在模塊名稱和文件之間建立映射關系。
TypeScript 編譯器支持在 tsconfig.json 文件中使用 "pathes" 屬性申明類似的映射。
示例
導入模塊 "jquery" 會在運行時轉換為 "node_modules/jquery/dist/jquery.slim.min.js" .
{
"compilerOptions": {
"baseUrl": "./node_modules",
"paths": {
"jquery": ["jquery/dist/jquery.slim.min"]
}
}
"paths" 也可以進行復雜的映射,比如回退了多級的位置。想像一下,某個項目配置了一些模塊在某個位置,而其它的模塊在另一個位置。
rootDirs 帶來的虛擬目錄
可以用 'rootDirs' 通知編譯器把 根 都當作一個“虛擬”目錄;然后編譯器可以把所有“虛擬”目錄假設為一個目錄,并在此通過相對路徑找到導入的模塊。
示例
假設有這樣一個項目結構
src
└── views
└── view1.ts (imports './template1')
└── view2.ts
generated
└── templates
└── views
└── template1.ts (imports './view2')</code></pre>
某個構建步驟會從 /src/views 和 /generated/templates/views 拷貝到輸出目錄中的同一個目錄里。在運行的時候,視圖希望模板就在它的同級目錄下,這樣就可以使用相對名稱 "./template" 來導入了。
"rootDirs" 指定了一個 根 列表,包含了期望在運行時放在一起的內容。在這個示例中, tsconfig.json 文件看起來就像這樣:
{
"compilerOptions": {
"rootDirs": [
"src/views",
"generated/templates/views"
]
}
}
跟蹤模塊解決方案
traceResolution 提供了一個方便的方式來讓編譯器知道該如何找到模塊。
tsc --traceResolution
模塊申明的速配環境
如果你不想在使用一個新模塊的時候花時間去寫它的申明,你現在可以簡單地使用速配環境來達到目的。
declarations.d.ts
declare module "hot-new-module";
從速配模塊導入的變量都是 any 類型。
import x, {y} from "hot-new-module";
x(y);
模塊名稱中的通配符
之前想通過模塊加載器擴展(例如 AMD 或者 SystemJS ) 十分不易;以前需要為每個資源定義環境模塊申明。
TypeScript 2.0 支持使用通配符( * )申明一“組”模塊名稱;這種方法使得只需要為擴展申明一次,而不必為每個資源進行申明。
示例
declare module "*!text" {
const content: string;
export default content;
}
// 有些會用另一種形式
declare module "json!*" {
const value: any;
export default value;
}
這樣就可以導入與 "*!text" 或 "json!*" 匹配的資源。
import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);
在從無類型代碼中遷移代碼時,通配符模塊也非常有用。如果與模塊申明的速配環境結合,非常容易地就能將一系列的模塊當作 any 申明。
示例
declare module "myLibrary/*";
從 myLibrary 下的模塊導入的內容都被編譯器當作 any 類型;這直接關閉了這些模塊的形式或類型檢查。
import { readFile } from "myLibrary/fileSystem/readFile`;
readFile(); // readFile 是 'any'</code></pre>
支持 UMD 模塊定義
有些庫被設置為允許多處模塊加載器加載,或者不需要使用加載器(全局變量)。知名的有 UMD 和 Isomorphic 模塊。這些庫既可以通過 import 導入使用,也可以通過設置全局變量來使用。
例如:
math-lib.d.ts
export const isPrime(x: number): boolean;
export as namespace mathLib;
之后這個庫在模塊中通過導入使用:
import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // 錯誤: 不能在模塊內使用全局定義
它也可以當作全局變量使用,但只能在腳本中這樣做。(腳本指不包含導入導出的文件。)
mathLib.isPrime(2);
可選類屬性
現在類中可以定義可選的類屬性和方法,這在接口中早就實現并為大家所熟知了。
示例
class Bar {
a: number;
b?: number;
f() {
return 1;
}
g?(): number; // 可選方法的方法體可以省略掉
h?() {
return 2;
}
}
在 --strictNullChecks 模式下編譯時,可選屬性和方法的類型中會自動包含 undefined 。因此上面示例中的 b 屬性是 number | undefined 類型,而 g 方法是 (() => number) | undefined 類型。
類型控制會在適當的時機將 undefined 從類型中剝離出去:
function test(x: Bar) {
x.a; // number
x.b; // number | undefined
x.f; // () => number
x.g; // (() => number) | undefined
let f1 = x.f(); // number
let g1 = x.g && x.g(); // number | undefined
let g2 = x.g ? x.g() : 0; // number
}
私有構造函數和保護的構造函數
類構造函數可以申明為 private 或 protected 。具有私有構造函數的類不能在外部實例化,也不能被繼承。具有保護構造函數的類不能在外部實例化,但可以被繼承。
示例
class Singleton {
private static instance: Singleton;
private constructor() { }
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
let e = new Singleton(); // 錯誤: 'Singleton' 的構造函數是私有的
let v = Singleton.getInstance();</code></pre>
抽象屬性和訪問器
抽象類可以申明抽象屬性和抽象訪問器。子類中需要定義抽象屬性,或者繼續標記為抽象的。抽象屬性不能初始化。抽象訪問器不能有函數體。
示例
abstract class Base {
abstract name: string;
abstract get value();
abstract set value(v: number);
}
class Derived extends Base {
name = "derived";
value = 1;
}</code></pre>
隱含的索引特性
如果一個對象字面量的所有屬性都符合某個索引特性,那么這個對象字面量類型就就可以賦值給它。這樣對于需要一個映射或字典作為參數的函數,就可以接受初始化為相應對象字面量的變量了:
function httpService(path: string, headers: { [x: string]: string }) { }
const headers = {
"Content-Type": "application/x-www-form-urlencoded"
};
httpService("", { "Content-Type": "application/x-www-form-urlencoded" }); // 正確
httpService("", headers); // 現在正確,而以前是錯誤的</code></pre>
用lib 包含內置類型申明
輸入 --lib 可以讓 ES6/ES2015 內置 API 申明僅限于 target: ES6 。通過 --lib 選項你可以選擇一些內置 API 申明組包含在項目中。假如你希望運行時支持 Map 、 Set 和 Promise (大部分新瀏覽器都支持),只需要使用參數 --lib es2015.collection,es2015.promise 。
與之類似,也可以從項目中排除一些不需要的申明,比如你在 node 項目中就不需要包含 DOM,那么可以使用 --lib es5,es6 。
這里有一個支持的 API 組列表:
-
dom
-
webworker
-
es5
-
es6 / es2015
-
es2015.core
-
es2015.collection
-
es2015.iterable
-
es2015.promise
-
es2015.proxy
-
es2015.reflect
-
es2015.generator
-
es2015.symbol
-
es2015.symbol.wellknown
-
es2016
-
es2016.array.include
-
es2017
-
es2017.object
-
es2017.sharedmemory
-
scripthost
示例
tsc --target es5 --lib es5,es2015.promise
"compilerOptions": {
"lib": ["es5", "es2015.promise"]
}
noUnusedParameters和noUnusedLocals標記未使用的申明
TypeScript 2.0 有兩個參數可幫助你保持代碼簡潔。
--noUnusedParameters 參數會將未使用的函數和方法參數標記為錯誤。
--noUnusedLocals 會將未使用的局部(未導出)申明,包含變量、函數、類、導入等,標記出來。在使用 --noUnusedLocals 參數的情況下,未使用的私有類成員也會被標記為錯誤。
示例
import B, { readFile } from "./b";
// ^ 錯誤: B
申明但未使用
readFile();
export function write(message: string, args: string[]) {
// ^^^^ 錯誤: 'arg' 申明但未使用
console.log(message);
}</code></pre>
以 _ 開始的參數申明會被“未使用”參數檢查忽略。例如:
function returnNull(_a) { // 正確
return null;
}
模塊識別允許 .js 擴展名
TypeScript 2.0 以前,模塊識別會忽略擴展名。比如,導入 import d from "./moduleA.js" ,編譯器會在 ./moduleA.js.ts 或者 ./moduleA.js.d.ts 中查找 "moduleA.js" 中的定義。這使得通過 URI 標識來使用一些像 SystemJS 那樣的構建或加載工具比較困難。
TypeScript 2.0 的編譯器會在 ./moduleA.ts 或 ./moduleA.d.ts 中去查找 "moduleA.js" 中的定義。
支持target : es5同時使用module: es6
以前的版本中 target: es5 和 module: es6 參數不同合并使用,但現在可以了。這能促進使用基于 ES2015 的 Tree-Shaking 工具,比如 rollup 。
函數參數列表或調用參數列表后面的逗號
現在允許函數參數列表或調用參數列表后面出現逗號了。這在 Stage-3 ECMAScript 建議 中提出,對 ES3/ES5/ES6 均有效。
示例
function foo(
bar: Bar,
baz: Baz, // 允許參數列表后面的逗號
) {
// 實現...
}
foo(
bar,
baz, // 允許調用參數列表后面的逗號
);</code></pre>
新參數 skipLibCheck
TypeScript 2.0 添加了一個新編譯參數 --skipLibCheck ,這個參數會讓編譯器跳過對申明文件(擴展名是 .d.ts 的文件)的類型檢查。如果一個程序包含大量的申明文件,編譯器會花很多時間去檢查這些已知沒有錯誤的申明。如果跳過對這些申明文件的檢查,編譯時間會得到顯著提升。
由于一個文件中的申明可能影響其它文件的類型檢查,所以使用 --skipLibCheck 參數后可能會導致某些錯誤被不被探測到。比如,一個非申明文件使用了某個申明文件中申明的類型,那只有在申明文件被檢查的時候才可能發現并報告錯誤。當然這種情況極不容易發生。
允許不同的申明中重復申明標識符
在多個申明文件中為某個接口定義了相同的成員,這通常會導致重復定義錯誤。
TypeScript 2.0 放寬了這個限制。如果兩個定義塊中出現重復定義,只要它們是 完全相同的 類型就不會有問題。
在同一個定義塊中定義重復的類型仍然是錯誤的。
示例
interface Error {
stack?: string;
}
interface Error {
code?: string;
path?: string;
stack?: string; // 正確
}</code></pre>
新參數 declarationDir
declarationDir 允許在與生成的 JavaScript 文件不同的位置生成申明文件。
來自:https://segmentfault.com/a/1190000007002883