本篇文章除了要來介紹 undefined
、null
在 strictNullChecks
的不同設置下的差異
還會介紹到「兩者的可指派性」和 「TypeScript 的特殊語法 - 非空斷言操作符(!
)」
🔗 本文有提到的其他型別,文章連結整理在這
null 和 undefined 都是 JavaScript 的原始型別 (primitive type)
在 TypeScript 中,undefined、null 的行為會由 tsconfig.json 中的 strictNullChecks
是開啟還是關閉而有不同
// tsconfig.json
"compilerOptions": {
"strictNullChecks": false,
// ... 略 ...
}
let str: string = null; // ✅ Pass
let num: number = undefined; // ✅ Pass
a
和 變數 b
接會被推斷為 any
let a = null;
let b = undefined;
strict
為 true
時,strictNullChecks 會自動被啟用// tsconfig.json
"compilerOptions": {
"strictNullChecks": true,
// ... 略 ...
}
a
和 變數 b
會被分別推斷為 undefined
和 null
let a = undefined; // : undefined
let b = null; // : null
let str: string = null; // ❌ Type 'null' is not assignable to type 'string'.
let num: number = undefined; // ❌ Type 'undefined' is not assignable to type 'number'.
我們已經知道當開啟 strictNullChecks 時,null 和 undefined 僅能賦值給自己,但其實也可以利用「聯合型別」(如 string | null 或 number | undefined)來巧妙避開檢查機制
想了解「聯合型別」,歡迎看之前寫的文章
🔗 聯合型別和交集型別
function doSomething(x: string | null) {
if (x === null) {
// ... 略 ...
} else {
console.log(x.toUpperCase()); // ✅ Pass
}
}
如果環境狀況允許,官方建議把 strictNullChecks 打開,以防非預期錯誤發生
非空斷言操作符(!
)是 TypeScript 的特殊語法,在任何表達式後加上 !
,表示會忽略對 null
或 undefined
的型別檢查,告訴 TypeScript :「我知道值不會是 null 或 undefined」
以下方例子來說,當操作變數的時候,就算有給定初始值,編譯時仍然會報錯,因為 a
還是有機會會出現 null
function test(a: string | null = 'apple') {
console.log(a.toUpperCase()); // ❌ 'a' is possibly 'null'.
}
如果要避免以上情況發生,除了可以用 typeof
, instantOf
等來 narrowing type,還有更簡潔的方式就是加上 !
:
function test(a: string | null = 'apple') {
console.log(a!.toUpperCase()); // ✅ Pass
}
function liveDangerously(x?: number | null) {
console.log(x!.toFixed()); // ✅ Pass
}
💡 !
不能濫用
如果在 !
的使用上過於大意,可能在運行時,會出現 TypeError
錯誤
建議使用「條件檢查」或 「Optional Chaining ?.
」相比於使用 !
更安全
function liveDangerously(x?: number | null) {
console.log(x!.toFixed()); // ✅ Pass
}
// 傳入 null 或 undefined,會在運行時報錯 ❌ TypeError: Cannot read properties of null (reading 'toFixed')
liveDangerously(null);
liveDangerously(undefined);
圖片來源
又是這張從 Day16 就開始出現的圖,不知道有沒有人跟我一樣始終覺得這張圖看起來不怎抹直觀
橫軸是「目標型別」;縱軸是「來源型別」
藍色勾勾(✓):來源型別可以被指派給目標型別
綠色勾勾(✓):tsconfig.json 中的 strictNullChecks 關閉時才能被指派
這裡就先專注看 undefined 和 nul 就好,這張圖想告訴我們
官方文件上還補充道
當 strictNullChecks 關閉時,null 和 undefined 的行為與 never
類似,它們可以指派給大多數型別,但大多數型別無法指派給它們,畢竟 never 是屬於 the bottom type(底層型別),越底層的型別,能夠接受的範圍越窄
當 strictNullChecks 開啟時,null 和 undefined 的行為與 void
類似,它們無法被指派給其他型別,也無法將其他型別指派給它們,除了 any、unknown 和 void 外(undefined 始終可以賦值給 void)
以下範例是 strictNullChecks 開啟時會報的錯
let strVal = 'hi';
let nullVal = null;
let undefinedVal = undefined;
strVal = nullVal; // ❌ Type 'null' is not assignable to type 'string'.
nullVal = strVal; // ❌ Type 'string' is not assignable to type 'null'.
undefinedVal = strVal; // ❌ Type 'strVal' is not assignable to type 'undefined'.
undefinedVal = nullVal; // ❌ Type 'null' is not assignable to type 'undefined'.
以上其實也不用硬背,單純順手把表格的內容整理一下,踩坑踩多了自然慢慢就會記起來了
strictNullChecks
都設為開啟
,更為安全非空斷言操作符 (!)
是 TypeScript 的特殊語法,在告訴編譯器說:「可以忽略 null 或 undefined 的型別檢查」,要謹慎使用,它可能造成運行時出現 TypeError
每天的內容有推到 github 上喔