天天看點

TypeScript 2.0 标記聯合類型

假設咱們為系統使用者可以選擇的以下支付方式模組化

cash (現金)

paypal 與給定的電子郵件位址

credit card 帶有給定卡号和安全碼

對于這些支付方法,咱們可以建立一個 typescript 接口

注意,除了必需的資訊外,每種類型都有一個 kind 屬性,即所謂的判别屬性。這裡每種情況都是字元串字面量類型。

現在定義一個 paymentmethod 類型,它是我們剛才定義的三種類型的并集。通過這種方式,用聲明 paymentmethod 每個變量, 必須具有給定的三種組成類型中的一種:

現在我們的類型已經就緒,來編寫一個函數來接受付款方法并傳回一個讀得懂的話語:

首先,該函數包含的類型注釋很少,method 參數僅包含一個。除此之外,函數基本是純 es2015 代碼。

在 switch 語句的每個 case 中,typescript 編譯器将聯合類型縮小到它的一個成員類型。例如,當比對到 "paypal",method 參數的類型從 paymentmethod 縮小到 paypal。是以,咱們可以通路 email 屬性,而不必添加類型斷言。

本質上,編譯器跟蹤程式控制流以縮小标記聯合類型。除了 switch 語句之外,它還要考慮條件以及指派和傳回的影響。

控制流的類型分析 使得使用标記聯合類型非常順利。使用最少的 typescript 文法開銷,咱可以編寫幾乎純 js,并且仍然可以從類型檢查和代碼完成中受益。

标記聯合類型真正發揮作用的用例是在 typescript 應用程式中使用 redux 時。 編寫一個事例,其中包括一個模型,兩個 actions 和一個 todo 應用程式的 reducer。

以下是一個簡化的 todo 類型,它表示單個 todo。這裡使用 readonly 修飾符為了防止屬性被修改。

使用者可以添加新的 todos 并切換現有 todos 的完成狀态。根據這些需求,咱們需要兩個 redux 操作,如下所示:

與前面的示例一樣,現在可以将redux操作建構為應用程式支援的所有操作的聯合

在本例中,type 屬性充當判别屬性,并遵循redux中常見的命名模式。現在添加一個與這兩個action 一起工作的 reducer:

同樣,隻有函數簽名包含類型注釋。代碼的其餘部分是純 es2015,而不是特定于 typescript。

我們遵循與前面示例相同的邏輯。基于 redux 操作的 type 屬性,我們在不修改現有狀态的情況下計算新狀态。在 switch語句的情況下,我們可以通路特定于每個操作類型的 text 和 index 屬性,而不需要任何類型斷言。

typescript 2.0 引入了一個新原始類型 never。never 類型表示值的類型從不出現。具體而言,never 是永不傳回函數的傳回類型,也是變量在類型保護中永不為 true 的類型。

這些是 never 類型的确切特征,如下所述:

never 是所有類型的子類型并且可以指派給所有類型。

沒有類型是 never 的子類型或能指派給 never(never類型本身除外)。

在函數表達式或箭頭函數沒有傳回類型注解時,如果函數沒有 return 語句,或者隻有 never 類型表達式的 return語句,并且如果函數是不可執行到終點的(例如通過控制流分析決定的),則推斷函數的傳回類型是 never。

在有明确 never 傳回類型注解的函數中,所有 return 語句(如果有的話)必須有 never 類型的表達式并且函數的終點必須是不可執行的。

聽得雲裡霧裡的,接下來,用幾個例子來講講 never 這位大哥。

下面是一個永不傳回的函數示例:

該函數由一個不包含 break 或 return 語句的無限循環組成,是以無法跳出循環。是以,推斷函數的傳回類型是 never。

類似地,下面函數的傳回類型被推斷為 never

typescript 推斷出 never 類型,因為該函數既沒有傳回類型注釋,也沒有可到達的端點(由控制流分析決定)。

另一種情況是,never 類型被推斷為從不為 ture。在下面的示例中,我們檢查 value 參數是否同時是字元串和數字,這是不可能的。

這個例子顯然是過于作,來看一個更實際的用例。下面的示例展示了 typescript 的控制流分析縮小了類型守衛下變量的聯合類型。直覺地說,類型檢查器知道,一旦咱們檢查了 value 是字元串,它就不能是數字,反之亦然

注意,在最後一個 else 分支中,value 既不能是字元串,也不能是數字。在這種情況下,typescript 推斷出 never 類型,因為咱們已經将 value 參數注解為類型為 string | number,也就是說,除了string 或 number, value 參數不可能有其他類型。

一旦控制流分析排除了 string 和 number 作為 value 類型的候選項,類型檢查器就推斷出never 類型,這是惟一剩下的可能性。但是,咱們也就不能對 value 做任何有用的事情,因為它的類型是 never,是以咱們的編輯器工具不會顯示自動顯示提示該值有哪些方法或者屬性可用。

TypeScript 2.0 标記聯合類型

你可能會問,為什麼 typescript 已經有一個 void 類型為啥還需要 never 類型。雖然這兩者看起來很相似,但它們是兩個不同的概念:

沒有顯式傳回值的函數将隐式傳回 undefined 。雖然我們通常會說這樣的函數“不傳回任何東西”,但它會傳回。在這些情況下,我們通常忽略傳回值。這樣的函數在 typescript 中被推斷為有一個 void 傳回類型。

具有 never 傳回類型的函數永不傳回。它也不傳回 undefined。該函數沒有正常的完成,這意味着它會抛出一個錯誤,或者根本不會完成運作。

關于函數聲明的傳回類型推斷有一個小問題。咱們前面列出的幾條 never 特征,你會發現下面這句話:

在函數表達式或箭頭函數沒有傳回類型注解時,如果函數沒有return語句,或者隻有never類型表達式的return語句,并且如果函數是不可執行到終點的(例如通過控制流分析決定的),則推斷函數的傳回類型是never。

它提到了函數表達式和箭頭函數,但沒有提到函數聲明。也就是說,為函數表達式推斷的傳回類型可能與為函數聲明推斷的傳回類型不同:

這種行為的原因是向後相容性,如下所述。如果希望函數聲明的傳回類型 never ,則可以對其進行顯式注釋: