對比理解Typescript中的as、問號與歎號

as關鍵字表示斷言

Typescript中,表示斷言有兩種方式。一種是尖擴號表示法:html

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
複製代碼

另外一種使用as關鍵字:node

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
複製代碼

惟一區別是,在JSX中,尖擴號與JSX語法衝突,只能使用as關鍵字。typescript

問號(?)用於屬性定義

準備工做:如下示例能夠創建一個optional.ts的文件,而後用tsc進行編譯。tsc能夠用npm install -g typescript安裝。npm

//運行如下命令能夠在同目錄下生成optional.js,有錯誤時會報錯
tsc optional.ts
tsc --strictNullChecks optional.ts
複製代碼

問號表示可選的屬性,通常用於屬性定義,如,用於接口時:數組

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig) {
    if (config.clor) {
        console.log(config);
    }   
}
複製代碼

可選屬性的含義是:使用這個屬性時,要麼這個屬性名不存在,要麼必須符合屬性的類型定義(官方解釋)。好比上述createSquare函數編譯時會報error錯誤:函數

error TS2551: Property 'clor' does not exist on type 'SquareConfig'.
複製代碼

若是修改createSquare,將config.color的值改成undefined,會怎樣?this

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig) {
    config.color = undefined;
    if (config.color) {
        console.log(config);
    }   
}
複製代碼

這時並無編譯報錯!明明config.color定義的是string類型呀?spa

即使是添加--strictNullChecks進行編譯,也不會報錯。可見,可選屬性所定義的類型,並無被typescript嚴格地對待,默認並不檢查undefined。須要注意的是,將上述undefined改爲null時,普通編譯也不報錯,--strictNullChecks編譯會報以下錯誤:code

error TS2322: Type 'null' is not assignable to type 'string | undefined'
複製代碼

從這句報錯中,咱們能夠得出這樣的結論:可選屬性等價於一個union類型,union了undefined;不加--strictNullChecks編譯時,null能夠賦值給undfined類型。也就是說,SquareConfig的定義與下面的代碼等價:htm

interface SquareConfig {
    color: string|undefined;
    width: number|undefined;
}
複製代碼

下面比較一下可選屬性與正常屬性。再次修改createSquare,將color屬性修改成正常屬性。

interface SquareConfig {
    color: string;
    width?: number;
}
function createSquare(config: SquareConfig) {
    config.color = undefined;
    if (config.color) {
        console.log(config);
    }   
}    
複製代碼

--strictNullChecks編譯,報錯了:

error TS2322: Type 'undefined' is not assignable to type 'string'
複製代碼

這個比較也驗證了上述的結論。

問號(?)用於屬性讀取

如下示例都使用--strictNullChecks編譯

問號用於屬性讀取,主要有兩個場景:一是讀取數組元素(以下面的node[i]),二是讀取不肯定的類型如anyunion,可選類型(如node[i].data)等。以下例,保存爲index.ts

interface VNodeData {
    class?: string;
}
interface VNode {
    sel?: string;
    data?: VNodeData;
}
function test(node: VNode[]) {
    let i = 0;
    var b = node[i].data.class;
    if(b !== undefined) {
        console.log(1);
    }   
}
複製代碼

tsc --strictNullChecks index.ts,報錯:

error TS2532: Object is possibly 'undefined'
複製代碼

下面將一一展現這一行代碼var b = node[i].data.class;修改改後的效果。

一、修改成var b = node[i]?.data.class;,而後編譯。報錯:

Object is possibly 'undefined'
複製代碼

二、修改成var b = node[i]?.data?.class;,而後編譯。編譯經過,查看編譯後的對應代碼爲:

function test(node) {
    var _a, _b; 
    var i = 0;
    var b = (_b = (_a = node[i]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b["class"];
    if (b !== undefined) {
        console.log(1);
    }
}
複製代碼

摘出來看,var b = node[i]?表示,若是node[i]的值爲null或者undefined,則b等於undefined,不然b=node[i]

三、修改成var b = (node[i] as VNode).data?.class;,而後編譯。編譯經過,查看編譯後的對應代碼爲:

function test(node) {
    var _a;
    var i = 0;
    var b = (_a = node[i].data) === null || _a === void 0 ? void 0 : _a["class"];
    if (b !== undefined) {
        console.log(1);
    }
}
複製代碼

此時,使用node[i]時,Typescript編譯器將再也不對其判斷是否爲nullundefined。即:var b = node[i] as VNode直接會被編成var b = node[i]

四、修改成var b = node[i]!.data?.class,而後編譯。編譯經過,查看編譯後的對應代碼爲:

function test(node) {
    var _a;
    var i = 0;
    var b = (_a = node[i].data) === null || _a === void 0 ? void 0 : _a["class"];
    if (b !== undefined) {
        console.log(1);
    }
}
複製代碼

可見,3和4的編譯後代碼徹底同樣,!的做用此時與as是等價的。然而,!只是用來判斷nullundefinedas則可用於變動(縮小或者放大均可以)類型檢測範圍,僅當as後面跟的類型是一個非空類型時,二者纔等價。以下例中,不能將as用法改成!

interface Cat {
    action: string;
}
interface Dog {
    action: string;
}
type Animal = Cat | Dog;
let action:Animal = {} as Cat;
複製代碼

結論

一、as!用於屬性的讀取,均可以縮小類型檢查範圍,都作判空用途時是等價的。只是!具體用於告知編譯器此值不可能爲空值(nullundefined),而as不限於此。

二、?可用於屬性的定義和讀取,讀取時告訴編譯器此值可能爲空值(nullundefined),須要作判斷。

相關文章
相關標籤/搜索