ts的代碼如下:
let foo = () =>{
let name = "alberto";
return name!;
}
document.body.innerHTML = foo();
編譯成js后的代碼如下:
var foo = function () {
var name = "alberto";
return name;
};
document.body.innerHTML = foo();
不理解name變量后面的感嘆號是什么意思?
Typescript中的as、問號與感嘆號
1、as關鍵字表示斷言
在Typescript中,表示斷言有兩種方式。一種是擴號表示法:
let someValue: any = "this is a string"; let strLength: number = (someValue).length;
- 1
- 2
另一種使用as關鍵字:
let someValue: any = "this is a string"; let strLength: number = (someValue as string).length;
- 1
- 2
2、問號(?)用於屬性定義
問號表示可選的屬性,一般用於屬性定義,如,用於接口時:
interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig) { if (config.clor) { console.log(config); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
可選屬性的含義是:使用這個屬性時,要么這個屬性名不存在,要么必須符合屬性的類型定義
比如上述createSquare函數編譯時會報error錯誤:
error TS2551: Property 'clor' does not exist on type 'SquareConfig'.
- 1
如果修改createSquare,將config.color的值改為undefined,會怎樣?
interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig) { config.color = undefined; if (config.color) { console.log(config); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
這時並沒有編譯報錯!明明config.color定義的是string類型呀?
即便是添加–strictNullChecks進行編譯,也不會報錯。可見,可選屬性所定義的類型,並沒有被typescript嚴格地對待,默認並不檢查undefined。需要注意的是,將上述undefined改成null時,普通編譯也不報錯,–strictNullChecks編譯會報如下錯誤:
error TS2322: Type 'null' is not assignable to type 'string | undefined'
- 1
從這句報錯中,我們可以得出這樣的結論:可選屬性等價於一個union類型,union了undefined;不加–strictNullChecks編譯時,null可以賦值給undfined類型。也就是說,SquareConfig的定義與下面的代碼等價:
interface SquareConfig { color: string|undefined; width: number|undefined; }
- 1
- 2
- 3
- 4
下面比較一下可選屬性與正常屬性。再次修改createSquare,將color屬性修改為正常屬性。
interface SquareConfig { color: string; width?: number; } function createSquare(config: SquareConfig) { config.color = undefined; if (config.color) { console.log(config); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
以–strictNullChecks編譯,報錯了:
error TS2322: Type ‘undefined’ is not assignable to type ‘string’
這個比較也驗證了上述的結論。
問號(?)用於屬性讀取
問號用於屬性讀取,主要有兩個場景:一是讀取數組元素(如下面的node[i]),二是讀取不確定的類型如any,union,可選類型(如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); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
用tsc --strictNullChecks index.ts,報錯:.
error TS2532: Object is possibly 'undefined'
- 1
下面將一一展示這一行代碼var b = node[i].data.class;修改改后的效果。
1、修改為var b = node[i]?.data.class;,然后編譯。報錯:
Object is possibly 'undefined'
- 1
2、修改為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"]; // var b = node[i].data.class;//報錯 if (b !== undefined) { console.log(1); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
var b = node[i]?表示,如果node[i]的值為null或者undefined,則b等於undefined,否則b=node[i]。
3、修改為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"]; // var b = node[i]?.data?.class; // var b = node[i].data.class;//報錯 if (b !== undefined) { console.log(1); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
此時,使用node[i]時,Typescript編譯器將不再對其判斷是否為null和undefined。即:var b = node[i] as VNode直接會被編成var b = node[i]。
4、修改為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"]; // var b = (node[i] as VNode).data?.class // var b = node[i]?.data?.class; // var b = node[i].data.class;//報錯 if (b !== undefined) { console.log(1); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
可見,3和4的編譯后代碼完全一樣,!的作用此時與as是等價的。然而,!只是用來判斷null和undefined;as則可用於變更(縮小或者放大都可以)類型檢測范圍,僅當as后面跟的類型是一個非空類型時,兩者才等價。如下例中,不能將as用法改為!。
interface Cat { action: string; } interface Dog { action: string; } type Animal = Cat | Dog; let action:Animal = {} as Cat;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
結論
1、as和!用於屬性的讀取,都可以縮小類型檢查范圍,都做判空用途時是等價的。只是!具體用於告知編譯器此值不可能為空值(null和undefined),而as不限於此。
2、?可用於屬性的定義和讀取,讀取時告訴編譯器此值可能為空值(null和undefined),需要做判斷。
一、感嘆號
1. 一個感嘆號
一個感嘆號(!)最好理解,也是我們經常使用的。!可以將變量轉為boolean值,所有的truthy和falsy都可以被轉化,比如null、undefined、數字0和空字符串取反都為true,其余都為false。
!null = true !undefined = true !"" = true !0 = true !100 = false !"asdss" = false
- 1
- 2
- 3
- 4
- 5
- 6
注意:truthy和falsy不是true和false
2. 兩個感嘆號
兩個感嘆號(!!),學習時比較少見到,通常用來判斷變量是不是truthy或者falsy,這么說好像和前面一樣?不,一個感嘆號只是單個判斷比如只對null或者只對undefined,使用兩個!!可以對所有truthy和falsy判斷,比如:
let a; // null、undefined、''、0 if (a !== null && typeof(a) !== "undefined" && a !== undefined && a !== '' && a !== 0){ //a為truthy時才執行,或者說a為真值時才執行 }
- 1
- 2
- 3
- 4
- 5
let a; if(!!a){ //a為truthy時才執行,或者說a為真值時才執行 }
- 1
- 2
- 3
- 4
這兩種方法的功能一樣,但是明顯下面一種更加簡潔。
好像還有三個!!!的,但是並沒有什么用處。似乎是將1和2都做了一遍,然后回到原點。
二、3個點(…)
三個點用於傳參時可以看作是Rest參數的逆運算,將一個數組轉為用逗號分隔的參數數組,就是說將參數數組展開了傳入函數中。
let a = [1,2,3] function f(x1,x2,x3){ ... } f(...a) // 等價於 f(1,2,3)
- 1
- 2
- 3
- 4
- 5
Rest參數和arguments對象的區別:
- rest參數只包括那些沒有給出名稱的參數,arguments包含所有參數
- arguments 對象不是真正的數組,而rest 參數是數組實例,可以直接應用sort, map, forEach, pop等方法
- arguments 對象擁有一些自己額外的功能
當用於函數形參時,后面就不能有其他參數,如下將會報錯
function func(a, ...b, c) { // ... } // Rest parameter must be last formal parameter
- 1
- 2
- 3
- 4
使用三個點,在許多時候可以代替數組的apply方法
// ES6以前 Math.max.apply(null, [22, 2, 222]) // ES6 Math.max(...[22, 2, 222]) // 等價於 Math.max(22, 2, 222);