【TypeScript 演化史 — 1】non-nullable 的類型
- 2019 年 11 月 13 日
- 筆記
在這篇文章中,我們將討論發佈於 TypeScript 2.0 中的 non-nullable
類型,這是對類型系統的一個重大的改進,該特性可對 null
和 undefined
的檢查。cannot read property 'x' of undefined
和 undefined is not a function
在 JS 中是非常常見的錯誤,non-nullable
類型可以避免此類錯誤。
null 和 undefined 的值
在 TypeScript 2.0 之前,類型檢查器認為 null
和 undefined
是每種類型的有效值。基本上,null
和 undefined
可以賦值給任何東西。這包括基本類型,如字元串、數字和布爾值:
let name: string; name = "Marius"; // OK name = null; // OK name = undefined; // OK let age: number; age = 24; // OK age = null; // OK age = undefined; // OK let isMarried: boolean; isMarried = true; // OK isMarried = false; // OK isMarried = null; // OK isMarried = undefined; // OK
以 number
類型為例。它的域不僅包括所有的IEEE 754浮點數,而且還包括兩個特殊的值 null
和 undefined
對象、數組和函數類型也是如此。無法通過類型系統表示某個特定變數是不可空的。幸運的是,TypeScript 2.0 解決了這個問題。
嚴格的Null檢查
TypeScript 2.0 增加了對 non-nullable 類型的支援,並新增嚴格 null
檢查模式,可以通過在命令行上使用 ——strictNullChecks
標誌來選擇進入該模式。或者,可以在項目中的 tsconfig.json 文件啟用 strictnullcheck
啟用。
{ "compilerOptions": { "strictNullChecks": true // ... } }
在嚴格的 null
檢查模式中,null
和 undefined
不再分配給每個類型。null
和undefined
現在都有自己的類型,每個類型只有一個值

如果咱們在編譯前時啟用了嚴格的 null
檢查,如果將 null
或 undefined
分配給任何變數都會導致類型錯誤
// 使用 --strictNullChecks 編譯 let name: string; name = "Marius"; // OK name = null; // Error name = undefined; // Error let age: number; age = 24; // OK age = null; // Error age = undefined; // Error let isMarried: boolean; isMarried = true; // OK isMarried = false; // OK isMarried = null; // Error isMarried = undefined; // Error
那麼,如何在 TypeScript 2.0 中使變數為空?
用聯合類型構建可空性
由於在啟用嚴格的 null
檢查時,類型在默認情況下是不可空的,所以我們需要顯式指定可為空,並告訴類型檢查器我們希望哪些變數為空。我們通過構造一個包含 null
或undefined
類型的聯合類型來實現這一點
let name: string | null; name = "Marius"; // OK name = null; // OK name = undefined; // Error
注意,undefined
不是 name
變數的有效值,因為聯合類型不包含 undefined
類型
這種可空性方法的一大優點是,類型中哪些成員是可空的變得很明顯,並且可以自文檔化。以這個簡單的 User
類型為例:
type User = { firstName: string; lastName: string | undefined; }; let jane: User = { firstName: "Jane", lastName: undefined }; let john: User = { firstName: "John", lastName: "Doe" };
我們可以通過添加 ?
將 lastName
屬性設為可選。這樣就可以完全省略 lastName
屬性的定義。 此外,undefined
的類型會自動添加到聯合類型中。 因此,以下所有分配類型都是可以的:
type User = { firstName: string; lastName?: string; }; // 可以為 lastName 屬性分配一個字元串 let john: User = { firstName: "John", lastName: "Doe" }; // 或者 undefined let jane: User = { firstName: "Jane", lastName: undefined }; // 還可以省略 let jake: User = { firstName: "Jake" };
可為空類型的屬性訪問
如果對象的類型包括 null
或 undefined
,則訪問任何屬性都會產生編譯時錯誤:
function getLength(s: string | null) { // Error: Object 可能為空 return s.length; }
在訪問屬性之前,需要使用類型保護來檢查給定對象上的屬性訪問是否安全:
function getLength(s: string | null) { if (s === null) { return 0; } return s.length; }
TypeScript 是兼容 JS ,並支援條件表達式中的類型保護,所以這種方法也可以:
function getLength(s: string | null) { return s ? s.length : 0; }
使用可空類型的函數調用
如果試圖調用包含 null
或 undefined
類型的函數,則會產生編譯時錯誤。下面的callback
參數是可選的(注意?),所以它可能 undefined
。因此,它不能被直接調用
function doSomething(callback?: () => void) { // Error: 不能調用可能是 「undefined」 的對象 callback(); }
與在訪問屬性之前檢查對象類似,我們首先需要檢查函數是否具有非空值:
function doSomething(callback?: () => void) { if (callback) { callback(); } }
還可以用 typeof
檢查返回的值
function doSomething(callback?: () => void) { if (typeof callback === "function") { callback(); } }
總結
Non-nullable
類型是 TypeScript 類型系統的基礎和有價值的補充。它們允許對哪些變數和屬性可以為空進行精確構建。只有在類型保護將屬性訪問或函數調用確定為安全之後,才允許進行屬性訪問或函數調用,從而避免了許多編譯時的可空性錯誤。