今天我們就來深入討論 never 類型,并介紹可能遇到的情況。
1. never 的特點
TypeScript使用never關鍵字來表示邏輯上不應該發生的情況和控制流。實際上,我們在工作中不會常遇到使用 never 的情況,但是還是很有必要了解它是如何有助于 TypeScript 的類型安全的。
官方文檔對 never 的描述:
never 類型是任何類型的子類型,也可以賦值給任何類型;但是,沒有類型是never的子類型或可以賦值給never類型(never 本身除外)。
也就是說,可以將never類型的變量分配給任何其他變量,但不能將其他變量分配給never。下面來看一個例子:
const throwErrorFunc = () => { throw new Error("error") }; let neverVar: never = throwErrorFunc() const myString = "" const myInt:number = neverVar; neverVar = myString // Type 'string' is not assignable to type 'never'
我們可以暫時忽略 throwErrorFunc 的功能,只需知道,這樣可以初始化類型為 never 的變量。
從上面的代碼中可以看到,可以將 never 類型的變量 neverVar 分配給 number 類型的變量myInt。但是,不能將 string 類型的 myString 變量分配給 neverVar,這樣分配會報錯。這也就是上面所說的,不能將任何其他類型的變量分配給 never,即使是 any 類型的變量。
2. 函數中的 never
TypeScript 使用 never 作為那些無法達到的終點的函數的返回值類型。主要有兩種情況:
函數拋出一個錯誤異常。
函數包含一個無限循環。
來看上面提到的 throwErrorFunc 函數,TypeScript 就會推斷此函數的返回類型為 never:
const throwErrorFunc = () => { throw new Error("error") };
另一種情況就是如果有一個一直為真的表達式產生了無限循環,它沒有中斷或者返回語句。TypeScript 就會推斷此函數的返回類型為 never:
const output = () => { while (true) { console.log("循環"); } };
3. never 和 void 的區別
那什么是 void 類型呢?我們有 void 為什么還要 never 類型呢?
never 和 void 的主要區別在于,void 類型的值可以是 undefined 或 null。
TypeScript 對不返回任何內容的函數使用 void。如果沒有為函數指定返回類型,并且在代碼中沒有返回任何內容,TypeScript 將推斷其返回類型為void。在TypeScript中,不返回任何內容的 void 函數實際上返回的是 undefined。
來看一個例子:
const functionWithVoidReturnType = () => {}; console.log(functionWithVoidReturnType()); // undefined
這里,TypeScript 會推斷此函數的返回類型為 void。我們通常會忽略 void 函數的返回值。
這里需要注意:根據 never 類型的特征,我們不能將 void 指定給 never:
const myVoidFunction = () => {} neverVar = myVoidFunction() // ERROR: Type 'never' is not assignable to type 'void'
4. never 作為可變類型守
如果變量被一個永遠不可能為 true 的類型保護縮小范圍,那么變量就可能成為 never類型。通常,這表明條件邏輯存在缺陷。
來看下面的例子:
const unExpectedResult = (myParam: "this" | "that") => { if (myParam === "this") { } else if (myParam === "that") { } else { console.log({ myParam }) } }
在這個例子中,當函數執行到 console.log({ myParam }) 時,myParam 的類型將為 never。
這是因為我們將 myParam 的類型設置為了 this 或 that。由于 TypeScript 認為 myParam 類型屬于兩個類型之一,所以從邏輯上講,第三個 else 語句永遠不會出現。所以TypeScript 就會將參數類型設置為 never。
5. 詳盡檢查
在實踐中可能會用到 never 的一個地方就是進行詳細的檢查。這有助于確保我們處理了代碼中的每個邊緣情況。
下面來看看如何使用詳盡檢查為 switch 語句添加更好的類型安全性:
type Animal = "cat" | "dog" | "bird" const shouldNotHappen = (animal: never) => { throw new Error("error") } const myPicker = (pet: Animal) => { switch(pet) { case "cat": { // ... return } case "dog": { // ... return } } return shouldNotHappen(pet) }
當添加 「return shouldNotHappen(pet)」時,會看到一個錯誤提示:
這里的錯誤提示告訴我們,忘記包含在 switch 語句中的情況。這是一種獲得編譯時安全性并確保處理 switch 語句中的所有情況的巧妙模式。
6. 結論如你所見,TypeScript 中的 never 類型在特定情況很有用。大多數情況下,never 表明代碼存在缺陷。
但在某些情況下,例如詳盡檢查,它可以成為幫助編寫更安全的 TypeScript 代碼的好工具。
原文地址:https://mp.weixin.qq.com/s/8rNKcxEXUmMGerAEGOlVCQ