〔譯〕TypeScript 2.0 的新特性

邊城發表於2019-02-16

原文是 What`s new in TypeScript 的一部分

可識別空和未定義型別

TypeScript 有兩個特殊的型別,null 和 undefined,nullundefined 分別是它們的值。以前是不能顯式使用這些型別的,但現在 nullundefined 不管在什麼型別檢查模式下都可以用作型別名稱。

以前的型別檢查器認為 nullundefined 可以賦值給任意變數。實際上,nullundefined每一個型別都是有效的值,任何型別都不可能明確定義不包含它們(因此不可能檢查到對它們的錯誤使用)。

--strictNullChecks

--strictNullChecks 引數用於新的嚴格空檢查模式。

在嚴格空檢查模式下, nullundefined 值都 屬於任何一個型別,它們只能賦值給自己這種型別或者 any (有一個例外,undefined 也可以賦值給 void)。因此,在常規型別檢查模式下 TT | ndefined 被認為是等同的(因為 undefined 被看作 T 的子型別),但它們在嚴格型別檢查模式下是不同的型別,只有 T | undefined 型別允許出現 undefined 值。TT | 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)型別控制

如果一個物件是 nullundefined,訪問它的屬性會引發編譯錯誤,對 nullundefined 進行函式呼叫也會引發編譯錯誤。不過型別檢查進行了擴充套件,支援對非空和非未定義型別進行檢查。

示例

// 使用 --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 != nullx === 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
    }
}

對帶點號和名稱的型別控制也會應用於使用者定義的型別控制功能,以及 typeofinstanceof 運算子,而且算不需要 --strictNullChecks 編譯引數。

對帶點號的名稱的型別控制會在對其中任意部分賦值後失敗。比如,x.y.z 的型別控制在 xx.yx.y.z 賦值後將失去效用。

表示式運算子

表示式運算子允許運算元型別包括 null 或/和 undefined,但結果一定是非空非未定義的型別。

// 使用 --strictNullChecks 引數編譯
function sum(a: number | null, b: number | null) {
    return a + b;  // 結果型別是 number
}

&& 運算子的會根據在運算元的型別來新增 null 或/和 undefined 型別到右運算元的型別中。|| 則會從左運算元的型別中去掉 nullundefined 後,再用於推導結果型別。

// 使用 --strictNullChecks 引數編譯
interface Entity {
    name: string;
}
let x: Entity | null;
let s = x && x.name;  // s 型別是 string | null
let y = x || { name: "test" };  // y 型別是 Entity

型別擴充套件

在嚴格空檢查模型下,nullundefined 不會 擴充套件為 any

let z = null;  // z 是 null

因為型別擴充套件,在常規型別檢查模式下 z 被推導為 any,但在嚴格空型別檢查模式下對 z 的型別推導結果仍然是 null(並且,由於沒有指定型別,nullz 唯一可能的值)。

非空斷言運算子

新的 ! 後置運算子用於斷言它的運算元在檢查器不能推斷的情況下是非空非未定義的。舉例說明:x! 申明 x 的值不可能是 nullundefined。與 <T>xx as T 這兩種形式的型別申明相似,在生成 JavaScript 程式碼時只是簡單地去掉了 ! 非空斷言運算子。

// 使用 --strictNullChecks 引數編譯
function validateEntity(e?: Entity) {
    // 如果 e 是 null 或者無效的 Entity,丟擲異常
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // 斷言 e 非空,然後訪問它的 name 屬性
}

相容性

這個新特性設定為可以在嚴格空檢查模式和常規型別檢查模式下都可使用。具體來說,在常規型別檢查模式下,nullundefined 型別會自動從聯合型別中剔除(因為它們已經是其它型別的子型別了),! 非空斷言運算子允許存在但在常規型別檢查模式下不會有任何作用。這樣一來,使用了非空非未定義型別的申明檔案就可以向後相容,在常規型別檢查模型下使用。

在實際應用中,嚴格空檢查模式要求所有用於編譯的檔案都是可識別空和未定義的。

基於控制流的型別分析

TypeScript 2.0 實現了基於控制流的型別分析,用於控制區域性變數和引數。之前,用於型別控制的型別分析侷限於 if 語句和 ?: 條件表示式,並不能用於賦值和控制流結構,如 returnbreak 語句。不管這個擁有聯合型別的變數和引數出現在什麼地方,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 型別
}

基於控制流的型別分析與 --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
}

標識屬性型別控制 來自於諸如 x.p == vx.p === vx.p != vx.p !== v 這樣的表示式,pv 是 string 字面型別〔譯者注:常量〕或者是一個 string 字面常量的聯合〔譯者注:比如 “type1” | “type2” | “type3” 這樣〕。x 有一個 p 屬性,該屬性有一個可能的值 v,標識屬性型別控制據此可以推斷 x 更精確的型別。

注意,目前我們僅支援標識屬性是 string 字面量型別的情況。我們計劃後面新增對布林和數值字面量的支援。

never 型別

TypeScript 2.0 引入了新的基本型別 never
never 型別值表現為從未發生。 具體說來,never 用於函式的返回值申明,而這個函式實際沒有返回任何東西。在型別控制作用下,never 是不可能作為變數型別的。

never 型別有如下一些特徵:

  • never 是所有型別的子型別,即可以賦值給任意型別。
  • 沒有任何型別是 never 的子型別,所以不能賦值給 nevernever 自己除外)。
  • 在沒有申明返回型別的函式表示式和箭頭函式中,如果沒有 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) {
    }
}

一些使用返回 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");
}

由於 never 可以賦值給任意型別,返回 never 的函式可以用於返回特定型別的回撥函式:

function test(cb: () => string) {
    let s = cb();
    return s;
}

test(() => "hello");
test(() => fail());
test(() => { throw new Error(); })

只讀屬性和只讀索引

現在通過 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 的(只讀)別名
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_modulesbower_componentsjspm_packages 目錄。

增強的模組解決方案:基本URL,路徑對映,根目錄和跟蹤

TypeScript 2.0 提供了一系列的模組解決方案工具來通知編譯器在哪裡找到給定模組的申明。

參閱 模組解決方案文件進行具體瞭解。

基礎URL

baseUrl 是 AMD 模組載入系統常用的辦法,它描述了模組在執行時應該從哪一個目錄“展開”。所有未指定相對路徑的匯入都假定相對於 baseUrl

示例

{
  "compilerOptions": {
    "baseUrl": "./modules"
  }
}

匯入 "moduleA" 時會在 ./modules/moduleA 中查詢。

import A from "moduleA";

路徑對映

有時候模組並不直接放在 baseUrl 下。載入器使用一個對映配置在模組名稱和檔案之間建立對映關係。參閱
RequireJs 文件SystemJS 文件.

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`)

某個構建步驟會從 /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`

支援 UMD 模組定義

有些庫被設定為允許多處模組載入器載入,或者不需要使用載入器(全域性變數)。知名的有 UMDIsomorphic 模組。這些庫既可以通過 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
}

私有建構函式和保護的建構函式

類建構函式可以申明為 privateprotected。具有私有建構函式的類不能在外部例項化,也不能被繼承。具有保護建構函式的類不能在外部例項化,但可以被繼承。

示例

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();

抽象屬性和訪問器

抽象類可以申明抽象屬性和抽象訪問器。子類中需要定義抽象屬性,或者繼續標記為抽象的。抽象屬性不能初始化。抽象訪問器不能有函式體。

示例

abstract class Base {
    abstract name: string;
    abstract get value();
    abstract set value(v: number);
}

class Derived extends Base {
    name = "derived";

    value = 1;
}

隱含的索引特性

如果一個物件字面量的所有屬性都符合某個索引特性,那麼這個物件字面量型別就就可以賦值給它。這樣對於需要一個對映或字典作為引數的函式,就可以接受初始化為相應物件字面量的變數了:

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);  // 現在正確,而以前是錯誤的

--lib 包含內建型別申明

輸入 --lib 可以讓 ES6/ES2015 內建 API 申明僅限於 target: ES6。通過 --lib 選項你可以選擇一些內建 API 申明組包含在專案中。假如你希望執行時支援 MapSetPromise(大部分新瀏覽器都支援),只需要使用引數 --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);
}

_ 開始的引數申明會被“未使用”引數檢查忽略。例如:

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: es5module: es6 引數不同合併使用,但現在可以了。這能促進使用基於 ES2015 的 Tree-Shaking 工具,比如 rollup

函式引數列表或呼叫引數列表後面的逗號

現在允許函式引數列表或呼叫引數列表後面出現逗號了。這在 Stage-3 ECMAScript 建議 中提出,對 ES3/ES5/ES6 均有效。

示例

function foo(
  bar: Bar, 
  baz: Baz, // 允許引數列表後面的逗號
) {
  // 實現...
}

foo(
  bar,
  baz, // 允許呼叫引數列表後面的逗號
);

新引數 --skipLibCheck

TypeScript 2.0 新增了一個新編譯引數 --skipLibCheck,這個引數會讓編譯器跳過對申明檔案(副檔名是 .d.ts 的檔案)的型別檢查。如果一個程式包含大量的申明檔案,編譯器會花很多時間去檢查這些已知沒有錯誤的申明。如果跳過對這些申明檔案的檢查,編譯時間會得到顯著提升。

由於一個檔案中的申明可能影響其它檔案的型別檢查,所以使用 --skipLibCheck 引數後可能會導致某些錯誤被不被探測到。比如,一個非申明檔案使用了某個申明檔案中申明的型別,那只有在申明檔案被檢查的時候才可能發現並報告錯誤。當然這種情況極不容易發生。

允許不同的申明中重複申明識別符號

在多個申明檔案中為某個介面定義了相同的成員,這通常會導致重複定義錯誤。

TypeScript 2.0 放寬了這個限制。如果兩個定義塊中出現重複定義,只要它們是 完全相同的 型別就不會有問題。

在同一個定義塊中定義重複的型別仍然是錯誤的。

示例

interface Error {
    stack?: string;
}


interface Error {
    code?: string;
    path?: string;
    stack?: string;  // 正確
}

新引數 --declarationDir

--declarationDir 允許在與生成的 JavaScript 檔案不同的位置生成申明檔案。

相關文章