TypeScript 2.0介紹

2022-04-21 09:19 更新

Null和undefined類型

TypeScript現(xiàn)在有兩個(gè)特殊的類型:Null和Undefined, 它們的值分別是nullundefined。 以前這是不可能明確地命名這些類型的,但是現(xiàn)在 nullundefined不管在什么類型檢查模式下都可以作為類型名稱使用。

以前類型檢查器認(rèn)為nullundefined賦值給一切。實(shí)際上,nullundefined是每一個(gè)類型的有效值, 并且不能明確排除它們(因此不可能檢測(cè)到錯(cuò)誤)。

--strictNullChecks

--strictNullChecks可以切換到新的嚴(yán)格空檢查模式中。

在嚴(yán)格空檢查模式中,nullundefined不再屬于任何類型的值,僅僅屬于它們自己類型和any類型的值 (還有一個(gè)例外, undefined也能賦值給void)。因此,盡管在常規(guī)類型檢查模式下TT | undefined被認(rèn)為是相同的 (因?yàn)?nbsp;undefined被認(rèn)為是任何T的子類型),但是在嚴(yán)格類型檢查模式下它們是不同的, 并且僅僅 T | undefined允許有undefined值,TT | null的關(guān)系同樣如此。

示例

// 使用--strictNullChecks參數(shù)進(jìn)行編譯的
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1;  // 正確
y = 1;  // 正確
z = 1;  // 正確
x = undefined;  // 錯(cuò)誤
y = undefined;  // 正確
z = undefined;  // 正確
x = null;  // 錯(cuò)誤
y = null;  // 錯(cuò)誤
z = null;  // 正確
x = y;  // 錯(cuò)誤
x = z;  // 錯(cuò)誤
y = x;  // 正確
y = z;  // 錯(cuò)誤
z = x;  // 正確
z = y;  // 正確

使用前賦值檢查

在嚴(yán)格空檢查模式中,編譯器要求未包含undefined類型的局部變量在使用之前必須先賦值。

示例

// 使用--strictNullChecks參數(shù)進(jìn)行編譯
let x: number;
let y: number | null;
let z: number | undefined;
x;  // 錯(cuò)誤,使用前未賦值
y;  // 錯(cuò)誤,使用前未賦值
z;  // 正確
x = 1;
y = null;
x;  // 正確
y;  // 正確

編譯器通過執(zhí)行基于控制流的類型分析檢查變量明確被賦過值。在本篇文章后面會(huì)有進(jìn)一步的細(xì)節(jié)。

可選參數(shù)和屬性

可選參數(shù)和屬性會(huì)自動(dòng)把undefined添加到他們的類型中,即使他們的類型注解明確不包含undefined。例如,下面兩個(gè)類型是完全相同的:

// 使用--strictNullChecks參數(shù)進(jìn)行編譯
type T1 = (x?: number) => string;              // x的類型是 number | undefined
type T2 = (x?: number | undefined) => string;  // x的類型是 number | undefined

非null和非undefined類型保護(hù)

如果對(duì)象或者函數(shù)的類型包含nullundefined,那么訪問屬性或調(diào)用函數(shù)時(shí)就會(huì)產(chǎn)生編譯錯(cuò)誤。因此,對(duì)類型保護(hù)進(jìn)行了擴(kuò)展,以支持對(duì)非null和非undefined的檢查。

示例

// 使用--strictNullChecks參數(shù)進(jìn)行編譯
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
    f(x);  // 正確,這里的x類型是number
}
else {
    f(x);  // 錯(cuò)誤,這里的x類型是number?
}
let a = x != null ? f(x) : "";  // a的類型是string
let b = x && f(x);  // b的類型是 string | 0 | null | undefined

非null和非undefined類型保護(hù)可以使用==、!=、===!==操作符和nullundefined進(jìn)行比較,如x != nullx === undefined。對(duì)被試變量類型的影響準(zhǔn)確地反映了JavaScript的語(yǔ)義(比如,雙等號(hào)運(yùn)算符檢查兩個(gè)值無論你指定的是null還是undefined,然而三等于號(hào)運(yùn)算符僅僅檢查指定的那一個(gè)值)。

類型保護(hù)中的點(diǎn)名稱

類型保護(hù)以前僅僅支持對(duì)局部變量和參數(shù)的檢查?,F(xiàn)在類型保護(hù)支持檢查由變量或參數(shù)名稱后跟一個(gè)或多個(gè)訪問屬性組成的“點(diǎn)名稱”。

示例

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
    }
}

點(diǎn)名稱的類型保護(hù)和用戶定義的類型保護(hù)函數(shù),還有typeofinstanceof操作符一起工作,并且不依賴--strictNullChecks編譯參數(shù)。

對(duì)點(diǎn)名稱進(jìn)行類型保護(hù)后給點(diǎn)名稱任一部分賦值都會(huì)導(dǎo)致類型保護(hù)無效。例如,對(duì)x.y.z進(jìn)行了類型保護(hù)后給x、x.yx.y.z賦值,都會(huì)導(dǎo)致x.y.z類型保護(hù)無效。

表達(dá)式操作符

表達(dá)式操作符允許運(yùn)算對(duì)象的類型包含null和/或undefined,但是總是產(chǎn)生非null和非undefined類型的結(jié)果值。

// 使用--strictNullChecks參數(shù)進(jìn)行編譯
function sum(a: number | null, b: number | null) {
    return a + b;  // 計(jì)算的結(jié)果值類型是number
}

&&操作符添加null和/或undefined到右邊操作對(duì)象的類型中取決于當(dāng)前左邊操作對(duì)象的類型,||操作符從左邊聯(lián)合類型的操作對(duì)象的類型中將nullundefined同時(shí)刪除。

// 使用--strictNullChecks參數(shù)進(jìn)行編譯
interface Entity {
    name: string;
}
let x: Entity | null;
let s = x && x.name;  // s的類型是string | null
let y = x || { name: "test" };  // y的類型是Entity

類型擴(kuò)展

在嚴(yán)格空檢查模式中,nullundefined類型是不會(huì)擴(kuò)展到any類型中的。

let z = null;  // z的類型是null

在常規(guī)類型檢查模式中,由于擴(kuò)展,會(huì)推斷z的類型是any,但是在嚴(yán)格空檢查模式中,推斷znull類型(因此,如果沒有類型注釋,nullz的唯一值)。

非空斷言操作符

在上下文中當(dāng)類型檢查器無法斷定類型時(shí),一個(gè)新的后綴表達(dá)式操作符!可以用于斷言操作對(duì)象是非null和非undefined類型的。具體而言,運(yùn)算x!產(chǎn)生一個(gè)不包含nullundefinedx的值。斷言的形式類似于<T>xx as T,!非空斷言操作符會(huì)從編譯成的JavaScript代碼中移除。

// 使用--strictNullChecks參數(shù)進(jìn)行編譯
function validateEntity(e?: Entity) {
    // 如果e是null或者無效的實(shí)體,就會(huì)拋出異常
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // 斷言e是非空并訪問name屬性
}

兼容性

這些新特性是經(jīng)過設(shè)計(jì)的,使得它們能夠在嚴(yán)格空檢查模式和常規(guī)類型檢查模式下都能夠使用。尤其是在常規(guī)類型檢查模式中,nullundefined類型會(huì)自動(dòng)從聯(lián)合類型中刪除(因?yàn)樗鼈兪瞧渌蓄愋偷淖宇愋停?code>!非空斷言表達(dá)式操作符也被允許使用但是沒有任何作用。因此,聲明文件使用null和undefined敏感類型更新后,在常規(guī)類型模式中仍然是可以向后兼容使用的。

在實(shí)際應(yīng)用中,嚴(yán)格空檢查模式要求編譯的所有文件都是null和undefined敏感類型。

基于控制流的類型分析

TypeScript 2.0實(shí)現(xiàn)了對(duì)局部變量和參數(shù)的控制流類型分析。以前,對(duì)類型保護(hù)進(jìn)行類型分析僅限于if語(yǔ)句和?:條件表達(dá)式,并且不包括賦值和控制流結(jié)構(gòu)的影響,例如returnbreak語(yǔ)句。使用TypeScript 2.0,類型檢查器會(huì)分析語(yǔ)句和表達(dá)式所有可能的控制流,在任何指定的位置對(duì)聲明為聯(lián)合類型的局部變量或參數(shù)產(chǎn)生最可能的具體類型(縮小范圍的類型)。

示例

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模式中尤為重要,因?yàn)榭煽疹愋褪褂寐?lián)合類型來表示:

function test(x: string | null) {
    if (x === null) {
        return;
    }
    x; // 在函數(shù)的剩余部分中,x類型是string
}

而且,在--strictNullChecks模式中,基于控制流的分析包括,對(duì)類型不允許為undefined的局部變量有明確賦值的分析。

function mumble(check: boolean) {
    let x: number; // 類型不允許為undefined
    x; // 錯(cuò)誤,x是undefined
    if (check) {
        x = 1;
        x; // 正確
    }
    x; // 錯(cuò)誤,x可能是undefi
    x = 2;
    x; // 正確
}

標(biāo)記聯(lián)合類型

TypeScript 2.0實(shí)現(xiàn)了標(biāo)記(或區(qū)分)聯(lián)合類型。具體而言,TS編譯器現(xiàn)在支持類型保護(hù),基于判別屬性的檢查來縮小聯(lián)合類型的范圍,并且switch語(yǔ)句也支持此特性。

示例

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語(yǔ)句中,s的類型在每一個(gè)case中都被縮小
    // 根據(jù)判別屬性的值,變量的其它屬性不使用類型斷言就可以被訪問
    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
}

判別屬性類型保護(hù)x.p == v、x.p === v、x.p != v或者x.p !== v其中的一種表達(dá)式,pv是一個(gè)屬性和字符串字面量類型或字符串字面量聯(lián)合類型的表達(dá)式。判別屬性類型保護(hù)縮小x的類型到由判別屬性pv的可能值之一組成的類型。

請(qǐng)注意,我們目前只支持字符串字面值類型的判別屬性。我們打算以后添加對(duì)布爾值和數(shù)字字面量類型的支持。

never類型

TypeScript 2.0引入了一個(gè)新原始類型never。never類型表示值的類型從不出現(xiàn)。具體而言,never是永不返回函數(shù)的返回類型,也是變量在類型保護(hù)中永不為true的類型。

never類型具有以下特征:

  • never是所有類型的子類型并且可以賦值給所有類型。
  • 沒有類型是never的子類型或能賦值給nevernever類型本身除外)。
  • 在函數(shù)表達(dá)式或箭頭函數(shù)沒有返回類型注解時(shí),如果函數(shù)沒有return語(yǔ)句,或者只有never類型表達(dá)式的return語(yǔ)句,并且如果函數(shù)是不可執(zhí)行到終點(diǎn)的(例如通過控制流分析決定的),則推斷函數(shù)的返回類型是never。
  • 在有明確never返回類型注解的函數(shù)中,所有return語(yǔ)句(如果有的話)必須有never類型的表達(dá)式并且函數(shù)的終點(diǎn)必須是不可執(zhí)行的。

因?yàn)?code>never是每一個(gè)類型的子類型,所以它總是在聯(lián)合類型中被省略,并且在函數(shù)中只要其它類型被返回,類型推斷就會(huì)忽略never類型。

一些返回never函數(shù)的示例:

// 函數(shù)返回never必須無法執(zhí)行到終點(diǎn)
function error(message: string): never {
    throw new Error(message);
}

// 推斷返回類型是never
function fail() {
    return error("Something failed");
}

// 函數(shù)返回never必須無法執(zhí)行到終點(diǎn)
function infiniteLoop(): never {
    while (true) {
    }
}

一些函數(shù)返回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");
}

因?yàn)?code>never可以賦值給每一個(gè)類型,當(dāng)需要回調(diào)函數(shù)返回一個(gè)更加具體的類型時(shí),函數(shù)返回never類型可以用于檢測(cè)返回類型是否正確:

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

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

只讀屬性和索引簽名

屬性或索引簽名現(xiàn)在可以使用readonly修飾符聲明為只讀的。

只讀屬性可以初始化和在同一個(gè)類的構(gòu)造函數(shù)中被賦值,但是在其它情況下對(duì)只讀屬性的賦值是不允許的。

此外,有幾種情況下實(shí)體隱式只讀的:

  • 屬性聲明只使用get訪問器而沒有使用set訪問器被視為只讀的。
  • 在枚舉類型中,枚舉成員被視為只讀屬性。
  • 在模塊類型中,導(dǎo)出的const變量被視為只讀屬性。
  • import語(yǔ)句中聲明的實(shí)體被視為只讀的。
  • 通過ES2015命名空間導(dǎo)入訪問的實(shí)體被視為只讀的(例如,當(dāng)foo當(dāng)作import * as foo from "foo"聲明時(shí),foo.x是只讀的)。

示例

interface Point {
    readonly x: number;
    readonly y: number;
}

var p1: Point = { x: 10, y: 20 };
p1.x = 5;  // 錯(cuò)誤,p1.x是只讀的

var p2 = { x: 1, y: 1 };
var p3: Point = p2;  // 正確,p2的只讀別名
p3.x = 5;  // 錯(cuò)誤,p3.x是只讀的
p2.x = 5;  // 正確,但是因?yàn)閯e名使用,同時(shí)也改變了p3.x
class Foo {
    readonly a = 1;
    readonly b: string;
    constructor() {
        this.b = "hello";  // 在構(gòu)造函數(shù)中允許賦值
    }
}
let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5;      // 錯(cuò)誤,元素是只讀的
b.push(5);     // 錯(cuò)誤,沒有push方法(因?yàn)檫@會(huì)修改數(shù)組)
b.length = 3;  // 錯(cuò)誤,length是只讀的
a = b;         // 錯(cuò)誤,缺少修改數(shù)組的方法

指定函數(shù)中this類型

緊跟著類和接口,現(xiàn)在函數(shù)和方法也可以聲明this的類型了。

函數(shù)中this的默認(rèn)類型是any。從TypeScript 2.0開始,你可以提供一個(gè)明確的this參數(shù)。this參數(shù)是偽參數(shù),它位于函數(shù)參數(shù)列表的第一位:

function f(this: void) {
    // 確保`this`在這個(gè)獨(dú)立的函數(shù)中無法使用
}

回調(diào)函數(shù)中的this參數(shù)

庫(kù)也可以使用this參數(shù)聲明回調(diào)函數(shù)如何被調(diào)用。

示例

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void;
}

this:void意味著addClickListener預(yù)計(jì)onclick是一個(gè)this參數(shù)不需要類型的函數(shù)。

現(xiàn)在如果你在調(diào)用代碼中對(duì)this進(jìn)行了類型注釋:

class Handler {
    info: string;
    onClickBad(this: Handler, e: Event) {
        // 哎喲,在這里使用this.在運(yùn)行中使用這個(gè)回調(diào)函數(shù)將會(huì)崩潰。
        this.info = e.message;
    };
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // 錯(cuò)誤!

--noImplicitThis

TypeScript 2.0還增加了一個(gè)新的編譯選項(xiàng)用來標(biāo)記函數(shù)中所有沒有明確類型注釋的this的使用。

tsconfig.json支持文件通配符

文件通配符來啦!!支持文件通配符一直是最需要的特性之一。

類似文件通配符的文件模式支持兩個(gè)屬性"include""exclude"。

示例

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "outFile": "../../built/local/tsc.js",
        "sourceMap": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ]
}

支持文件通配符的符號(hào)有:

  • *匹配零個(gè)或多個(gè)字符(不包括目錄)
  • ?匹配任意一個(gè)字符(不包括目錄)
  • **/遞歸匹配所有子目錄

如果文件通配符模式語(yǔ)句中只包含*.*,那么只匹配帶有擴(kuò)展名的文件(例如默認(rèn)是.ts.tsx.d.ts,如果allowJs設(shè)置為true.js.jsx也屬于默認(rèn))。

如果"files""include"都沒有指定,編譯器默認(rèn)包含所有目錄中的TypeScript文件(.ts、.d.ts.tsx),除了那些使用exclude屬性排除的文件外。如果allowJs設(shè)置為true,JS文件(.js.jsx)也會(huì)被包含進(jìn)去。

如果"files""include"都指定了,編譯器將包含這兩個(gè)屬性指定文件的并集。使用ourDir編譯選項(xiàng)指定的目錄文件總是被排除,即使"exclude"屬性指定的文件也會(huì)被刪除,但是files屬性指定的文件不會(huì)排除。

"exclude"屬性指定的文件會(huì)對(duì)"include"屬性指定的文件過濾。但是對(duì)"files"指定的文件沒有任何作用。當(dāng)沒有明確指定時(shí),"exclude"屬性默認(rèn)會(huì)排除node_modules、bower_componentsjspm_packages目錄。

模塊解析增加:BaseUrl、路徑映射、rootDirs和追蹤

TypeScript 2.0提供了一系列額外的模塊解析屬性告訴編譯器去哪里可以找到給定模塊的聲明。

更多詳情,請(qǐng)參閱模塊解析文檔。

Base URL

使用了AMD模塊加載器并且模塊在運(yùn)行時(shí)”部署“到單文件夾的應(yīng)用程序中使用baseUrl是一種常用的做法。所有非相對(duì)名稱的模塊導(dǎo)入被認(rèn)為是相對(duì)于baseUrl的。

示例

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

現(xiàn)在導(dǎo)入moduleA將會(huì)在./modules/moduleA中查找。

import A from "moduleA";

路徑映射

有時(shí)模塊沒有直接位于baseUrl中。加載器使用映射配置在運(yùn)行時(shí)去映射模塊名稱和文件,請(qǐng)參閱RequireJs文檔SystemJS文檔。

TypeScript編譯器支持tsconfig文件中使用"paths"屬性映射的聲明。

示例

例如,導(dǎo)入"jquery"模塊在運(yùn)行時(shí)會(huì)被轉(zhuǎn)換為"node_modules/jquery/dist/jquery.slim.min.js"。

{
  "compilerOptions": {
    "baseUrl": "./node_modules",
    "paths": {
      "jquery": ["jquery/dist/jquery.slim.min"]
    }
}

使用"paths"也允許更復(fù)雜的映射,包括多次后退的位置??紤]一個(gè)只有一個(gè)地方的模塊是可用的,其它的模塊都在另一個(gè)地方的項(xiàng)目配置。

rootDirs和虛擬目錄

使用rootDirs,你可以告知編譯器的根目錄組合這些“虛擬”目錄。因此編譯器在這些“虛擬”目錄中解析相對(duì)導(dǎo)入模塊,仿佛是合并到一個(gè)目錄中一樣。

示例

給定的項(xiàng)目結(jié)構(gòu)

 src
 └── views
     └── view1.ts (imports './template1')
     └── view2.ts

 generated
 └── templates
         └── views
             └── template1.ts (imports './view2')

構(gòu)建步驟將復(fù)制/src/views/generated/templates/views目錄下的文件輸出到同一個(gè)目錄中。在運(yùn)行時(shí),視圖期望它的模板和它存在同一目錄中,因此應(yīng)該使用相對(duì)名稱"./template"導(dǎo)入。

"rootDir"指定的一組根目錄的內(nèi)容將會(huì)在運(yùn)行時(shí)合并。因此在我們的例子,tsconfig.json文件應(yīng)該類似于:

{
  "compilerOptions": {
    "rootDirs": [
      "src/views",
      "generated/templates/views"
    ]
  }
}

追蹤模塊解析

--traceResolution提供了一種方便的方法,以了解模塊如何被編譯器解析的。

tsc --traceResolution

快捷外部模塊聲明

當(dāng)你使用一個(gè)新模塊時(shí),如果不想要花費(fèi)時(shí)間書寫一個(gè)聲明時(shí),現(xiàn)在你可以使用快捷聲明以便以快速開始。

declarations.d.ts

declare module "hot-new-module";

所有從快捷模塊的導(dǎo)入都具有任意類型。

import x, {y} from "hot-new-module";
x(y);

模塊名稱中的通配符

以前使用模塊加載器(例如AMDSystemJS)導(dǎo)入沒有代碼的資源是不容易的。之前,必須為每個(gè)資源定義一個(gè)外部模塊聲明。

TypeScript 2.0支持使用通配符符號(hào)(*)定義一類模塊名稱。這種方式,一個(gè)聲明只需要一次擴(kuò)展名,而不再是每一個(gè)資源。

示例

declare module "*!text" {
    const content: string;
    export default content;
}
// Some do it the other way around.
declare module "json!*" {
    const value: any;
    export default value;
}

現(xiàn)在你可以導(dǎo)入匹配"*!text""json!*"的東西了。

import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);

當(dāng)從一個(gè)基于非類型化的代碼遷移時(shí),通配符模塊的名稱可能更加有用。結(jié)合快捷外部模塊聲明,一組模塊可以很容易地聲明為any。

示例

declare module "myLibrary/*";

所有位于myLibrary目錄之下的模塊的導(dǎo)入都被編譯器認(rèn)為是any類型,因此這些模塊的任何類型檢查都會(huì)被關(guān)閉。

import { readFile } from "myLibrary/fileSystem/readFile`;

readFile(); // readFile是'any'類型

支持UMD模塊定義

一些庫(kù)被設(shè)計(jì)為可以使用多種模塊加載器或者不是使用模塊加載器(全局變量)來使用,這被稱為UMD同構(gòu)模塊。這些庫(kù)可以通過導(dǎo)入或全局變量訪問。

舉例:

math-lib.d.ts
export const isPrime(x: number): boolean;
export as namespace mathLib;

然后,該庫(kù)可作為模塊導(dǎo)入使用:

import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // 錯(cuò)誤:無法在模塊內(nèi)部使用全局定義

它也可以被用來作為一個(gè)全局變量,只限于沒有importexport腳本文件中。

mathLib.isPrime(2);

可選類屬性

現(xiàn)在可以在類中聲明可選屬性和方法,與接口類似。

示例

class Bar {
    a: number;
    b?: number;
    f() {
        return 1;
    }
    g?(): number;  // 可選方法的方法體可以省略
    h?() {
        return 2;
    }
}

--strictNullChecks模式下編譯時(shí),可選屬性和方法會(huì)自動(dòng)添加undefined到它們的類型中。因此,上面的b屬性類型是number | undefined,上面g方法的類型是(()=> number) | undefined。使用類型保護(hù)可以去除undefined。

私有的和受保護(hù)的構(gòu)造函數(shù)

類的構(gòu)造函數(shù)可以被標(biāo)記為privateprotected。私有構(gòu)造函數(shù)的類不能在類的外部實(shí)例化,并且也不能被繼承。受保護(hù)構(gòu)造函數(shù)的類不能再類的外部實(shí)例化,但是可以被繼承。

示例

class Singleton {
    private static instance: Singleton;

    private constructor() { }

    static getInstance() {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    } 
}

let e = new Singleton(); // 錯(cuò)誤:Singleton的構(gòu)造函數(shù)是私有的。
let v = Singleton.getInstance();

抽象屬性和訪問器

抽象類可以聲明抽象屬性和、或訪問器。所有子類將需要聲明抽象屬性或者被標(biāo)記為抽象的。抽象屬性不能初始化。抽象訪問器不能有具體代碼塊。

示例

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

class Derived extends Base {
    name = "derived";

    value = 1;
}

隱式索引簽名

如果對(duì)象字面量中所有已知的屬性是賦值給索引簽名,那么現(xiàn)在對(duì)象字面量類型可以賦值給索引簽名類型。這使得一個(gè)使用對(duì)象字面量初始化的變量作為參數(shù)傳遞給期望參數(shù)是map或dictionary的函數(shù)成為可能:

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);  // 現(xiàn)在可以,以前不可以。

使用--lib編譯參數(shù)包含內(nèi)置類型聲明

獲取ES6/ES2015內(nèi)置API聲明僅限于target: ES6。輸入--lib,你可以使用--lib指定一組項(xiàng)目所需要的內(nèi)置API。比如說,如果你希望項(xiàng)目運(yùn)行時(shí)支持Map、SetPromise(例如現(xiàn)在靜默更新瀏覽器),直接寫--lib es2015.collection,es2015.promise就好了。同樣,你也可以排除項(xiàng)目中不需要的聲明,例如在node項(xiàng)目中使用--lib es5,es6排除DOM。

下面是列出了可用的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標(biāo)記未使用的聲明

TypeScript 2.0有兩個(gè)新的編譯參數(shù)來幫助你保持一個(gè)干凈的代碼庫(kù)。-noUnusedParameters編譯參數(shù)標(biāo)記所有未使用的函數(shù)或方法的參數(shù)錯(cuò)誤。--noUnusedLocals標(biāo)記所有未使用的局部(未導(dǎo)出)聲明像變量、函數(shù)、類和導(dǎo)入等等,另外未使用的私有類成員在--noUnusedLocals作用下也會(huì)標(biāo)記為錯(cuò)誤。

示例

import B, { readFile } from "./b";
//     ^ 錯(cuò)誤:`B`聲明了,但是沒有使用。
readFile();


export function write(message: string, args: string[]) {
    //                                 ^^^^  錯(cuò)誤:'arg'聲明了,但是沒有使用。
    console.log(message);
}

使用以_開頭命名的參數(shù)聲明不會(huì)被未使用參數(shù)檢查。例如:

function returnNull(_a) { // 正確
    return null;
}

模塊名稱允許.js擴(kuò)展名

TypeScript 2.0之前,模塊名稱總是被認(rèn)為是沒有擴(kuò)展名的。例如,導(dǎo)入一個(gè)模塊import d from "./moduleA.js",則編譯器在./moduleA.js.ts./moduleA.js.d.ts中查找"moduleA.js"的定義。這使得像 SystemJS這種期望模塊名稱是URI的打包或加載工具很難使用。

使用TypeScript 2.0,編譯器將在./moduleA.ts./moduleA.d.ts中查找"moduleA.js"的定義。

支持編譯參數(shù)target : es5module: es6同時(shí)使用

之前編譯參數(shù)target : es5module: es6同時(shí)使用被認(rèn)為是無效的,但是現(xiàn)在是有效的。這將有助于使用基于ES2015的tree-shaking(將無用代碼移除)比如 rollup。

函數(shù)形參和實(shí)參列表末尾支持逗號(hào)

現(xiàn)在函數(shù)形參和實(shí)參列表末尾允許有逗號(hào)。這是對(duì)第三階段的ECMAScript提案的實(shí)現(xiàn), 并且會(huì)編譯為可用的 ES3/ES5/ES6。

示例

function foo(
  bar: Bar, 
  baz: Baz, // 形參列表末尾添加逗號(hào)是沒有問題的。
) {
  // 具體實(shí)現(xiàn)……
}

foo(
  bar,
  baz, // 實(shí)參列表末尾添加逗號(hào)同樣沒有問題
);

新編譯參數(shù)--skipLibCheck

TypeScript 2.0添加了一個(gè)新的編譯參數(shù)--skipLibCheck,該參數(shù)可以跳過聲明文件(以.d.ts為擴(kuò)展名的文件)的類型檢查。當(dāng)一個(gè)程序包含有大量的聲明文件時(shí),編譯器需要花費(fèi)大量時(shí)間對(duì)已知不包含錯(cuò)誤的聲明進(jìn)行類型檢查,通過跳過聲明文件的類型檢查,編譯時(shí)間可能會(huì)大大縮短。

由于一個(gè)文件中的聲明可以影響其他文件中的類型檢查,當(dāng)指定--skipLibCheck時(shí),一些錯(cuò)誤可能檢測(cè)不到。比如說, 如果一個(gè)非聲明文件中的類型被聲明文件用到, 可能僅在聲明文件被檢查時(shí)能發(fā)現(xiàn)錯(cuò)誤. 不過這種情況在實(shí)際使用中并不常見。

允許在聲明中重復(fù)標(biāo)識(shí)符

這是重復(fù)定義錯(cuò)誤的一個(gè)常見來源。多個(gè)聲明文件定義相同的接口成員。

TypeScript 2.0放寬了這一約束,并允許可以不同代碼塊中出現(xiàn)重復(fù)的標(biāo)識(shí)符, 只要它們有完全相同的類型。

在同一代碼塊重復(fù)定義仍不允許。

示例

interface Error {
    stack?: string;
}


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

新編譯參數(shù)--declarationDir

--declarationDir可以使生成的聲明文件和JavaScript文件不在同一個(gè)位置中。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)