詳解TypeScript2.0標(biāo)記聯(lián)合類型
使用標(biāo)記的聯(lián)合類型構(gòu)建付款方式
假設(shè)咱們?yōu)橄到y(tǒng)用戶可以選擇的以下支付方式建模
- Cash (現(xiàn)金)
- PayPal 與給定的電子郵件地址
- Credit card 帶有給定卡號和安全碼
對于這些支付方法,咱們可以創(chuàng)建一個 TypeScript 接口
interface Cash { kind: "cash"; } interface PayPal { kind: "paypal", email: string; } interface CreditCard { kind: "credit"; cardNumber: string; securityCode: string; }
注意,除了必需的信息外,每種類型都有一個kind屬性,即所謂的判別屬性。這里每種情況都是字符串字面量類型。
現(xiàn)在定義一個PaymentMethod類型,它是我們剛才定義的三種類型的并集。通過這種方式,用聲明PaymentMethod每個變量, 必須具有給定的三種組成類型中的一種:
type PaymentMethod = Cash | PayPal | CreditCard;
現(xiàn)在我們的類型已經(jīng)就緒,來編寫一個函數(shù)來接受付款方法并返回一個讀得懂的話語:
function describePaymentMethod(method: PaymentMethod) { switch (method.kind) { case "cash": // Here, method has type Cash return "Cash"; case "paypal": // Here, method has type PayPal return `PayPal (${method.email})`; case "credit": // Here, method has type CreditCard return `Credit card (${method.cardNumber})`; } }
首先,該函數(shù)包含的類型注釋很少,method參數(shù)僅包含一個。除此之外,函數(shù)基本是純 ES2015代碼。
在switch語句的每個case中,TypeScript 編譯器將聯(lián)合類型縮小到它的一個成員類型。例如,當(dāng)匹配到"paypal",method參數(shù)的類型從PaymentMethod縮小到PayPal。因此,咱們可以訪問email屬性,而不必添加類型斷言。
本質(zhì)上,編譯器跟蹤程序控制流以縮小標(biāo)記聯(lián)合類型。除了switch語句之外,它還要考慮條件以及賦值和返回的影響。
function describePaymentMethod(method: PaymentMethod) { if (method.kind === "cash") { // Here, method has type Cash return "Cash"; } // Here, method has type PayPal | CreditCard if (method.kind === "paypal") { // Here, method has type PayPal return `PayPal (${method.email})`; } // Here, method has type CreditCard return `Credit card (${method.cardNumber})`; }
控制流的類型分析使得使用標(biāo)記聯(lián)合類型非常順利。使用最少的 TypeScript語法開銷,咱可以編寫幾乎純js,并且仍然可以從類型檢查和代碼完成中受益。
使用標(biāo)記聯(lián)合類型構(gòu)建 Redux 操作
標(biāo)記聯(lián)合類型真正發(fā)揮作用的用例是在 TypeScript 應(yīng)用程序中使用Redux時。 編寫一個事例,其中包括一個模型,兩個actions和一個Todo應(yīng)用程序的reducer。
以下是一個簡化的Todo類型,它表示單個todo。這里使用readonly修飾符為了防止屬性被修改。
interface Todo { readonly text: string; readonly done: boolean; }
用戶可以添加新的 todos 并切換現(xiàn)有 todos 的完成狀態(tài)。根據(jù)這些需求,咱們需要兩個Redux操作,如下所示:
interface AddTodo { type: "ADD_TODO"; text: string; } interface ToggleTodo { type: "TOGGLE_TODO"; index: number }
與前面的示例一樣,現(xiàn)在可以將Redux操作構(gòu)建為應(yīng)用程序支持的所有操作的聯(lián)合
type ReduxAction = AddTodo | ToggleTodo;
在本例中,type屬性充當(dāng)判別屬性,并遵循Redux中常見的命名模式?,F(xiàn)在添加一個與這兩個action一起工作的Reducer:
function todosReducer( state: ReadonlyArray<Todo> = [], action: ReduxAction ): ReadonlyArray<Todo> { switch (action.type) { case "ADD_TODO": // action has type AddTodo here return [...state, { text: action.text, done: false }]; case "TOGGLE_TODO": // action has type ToggleTodo here return state.map((todo, index) => { if (index !== action.index) { return todo; } return { text: todo.text, done: !todo.done }; }); default: return state; } }
同樣,只有函數(shù)簽名包含類型注釋。代碼的其余部分是純 ES2015,而不是特定于 TypeScript。
我們遵循與前面示例相同的邏輯?;赗edux操作的type屬性,我們在不修改現(xiàn)有狀態(tài)的情況下計算新狀態(tài)。在switch語句的情況下,我們可以訪問特定于每個操作類型的text和index屬性,而不需要任何類型斷言。
never 類型
TypeScript 2.0引入了一個新原始類型never。never類型表示值的類型從不出現(xiàn)。具體而言,never是永不返回函數(shù)的返回類型,也是變量在類型保護(hù)中永不為true的類型。
這些是never類型的確切特征,如下所述:
- never是所有類型的子類型并且可以賦值給所有類型。
- 沒有類型是never的子類型或能賦值給never(never類型本身除外)。
- 在函數(shù)表達(dá)式或箭頭函數(shù)沒有返回類型注解時,如果函數(shù)沒有return語句,或者只有never類型表達(dá)式的return語句,并且如果函數(shù)是不可執(zhí)行到終點的(例如通過控制流分析決定的),則推斷函數(shù)的返回類型是never。
- 在有明確never返回類型注解的函數(shù)中,所有return語句(如果有的話)必須有never類型的表達(dá)式并且函數(shù)的終點必須是不可執(zhí)行的。
聽得云里霧里的,接下來,用幾個例子來講講never這位大哥。
永不返回的函數(shù)
下面是一個永不返回的函數(shù)示例:
// Type () => never const sing = function() { while (true) { console.log("我就是不返回值,怎么滴!"); console.log("我就是不返回值,怎么滴!"); console.log("我就是不返回值,怎么滴!"); console.log("我就是不返回值,怎么滴!"); console.log("我就是不返回值,怎么滴!"); console.log("我就是不返回值,怎么滴!"); } }
該函數(shù)由一個不包含break或return語句的無限循環(huán)組成,所以無法跳出循環(huán)。因此,推斷函數(shù)的返回類型是never。
類似地,下面函數(shù)的返回類型被推斷為never
// Type (message: string) => never const failwith = (message: string) => { throw new Error(message); };
TypeScript 推斷出never類型,因為該函數(shù)既沒有返回類型注釋,也沒有可到達(dá)的端點(由控制流分析決定)。
不可能有該類型的變量
另一種情況是,never類型被推斷為從不為ture。在下面的示例中,我們檢查value參數(shù)是否同時是字符串和數(shù)字,這是不可能的。
function impossibleTypeGuard(value: any) { if ( typeof value === "string" && typeof value === "number" ) { value; // Type never } }
這個例子顯然是過于作,來看一個更實際的用例。下面的示例展示了 TypeScript 的控制流分析縮小了類型守衛(wèi)下變量的聯(lián)合類型。直觀地說,類型檢查器知道,一旦咱們檢查了value是字符串,它就不能是數(shù)字,反之亦然
function controlFlowAnalysisWithNever( value: string | number ) { if (typeof value === "string") { value; // Type string } else if (typeof value === "number") { value; // Type number } else { value; // Type never } }
注意,在最后一個else分支中,value既不能是字符串,也不能是數(shù)字。在這種情況下,TypeScript 推斷出never類型,因為咱們已經(jīng)將value參數(shù)注解為類型為string | number,也就是說,除了string或number,value參數(shù)不可能有其他類型。
一旦控制流分析排除了string和number作為value類型的候選項,類型檢查器就推斷出never類型,這是惟一剩下的可能性。但是,咱們也就不能對value做任何有用的事情,因為它的類型是never,所以咱們的編輯器工具不會顯示自動顯示提示該值有哪些方法或者屬性可用。
never 和 void 之間的區(qū)別
你可能會問,為什么 TypeScript 已經(jīng)有一個void類型為啥還需要never類型。雖然這兩者看起來很相似,但它們是兩個不同的概念:
沒有顯式返回值的函數(shù)將隱式返回undefined。雖然我們通常會說這樣的函數(shù)“不返回任何東西”,但它會返回。在這些情況下,我們通常忽略返回值。這樣的函數(shù)在 TypeScript 中被推斷為有一個void返回類型。
具有never返回類型的函數(shù)永不返回。它也不返回undefined。該函數(shù)沒有正常的完成,這意味著它會拋出一個錯誤,或者根本不會完成運行。
函數(shù)聲明的類型推斷
關(guān)于函數(shù)聲明的返回類型推斷有一個小問題。咱們前面列出的幾條never特征,你會發(fā)現(xiàn)下面這句話:
在函數(shù)表達(dá)式或箭頭函數(shù)沒有返回類型注解時,如果函數(shù)沒有return語句,或者只有never類型表達(dá)式的return語句,并且如果函數(shù)是不可執(zhí)行到終點的(例如通過控制流分析決定的),則推斷函數(shù)的返回類型是never。
它提到了函數(shù)表達(dá)式和箭頭函數(shù),但沒有提到函數(shù)聲明。也就是說,為函數(shù)表達(dá)式推斷的返回類型可能與為函數(shù)聲明推斷的返回類型不同:
// Return type: void function failwith1(message: string) { throw new Error(message); } // Return type: never const failwith2 = function(message: string) { throw new Error(message); };
這種行為的原因是向后兼容性,如下所述。如果希望函數(shù)聲明的返回類型never,則可以對其進(jìn)行顯式注釋:
function failwith1(message: string): never { throw new Error(message); }
以上就是詳解TypeScript2.0標(biāo)記聯(lián)合類型的詳細(xì)內(nèi)容,更多關(guān)于TS2.0標(biāo)記聯(lián)合類型的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序簡單實現(xiàn)form表單獲取輸入數(shù)據(jù)功能示例
這篇文章主要介紹了微信小程序簡單實現(xiàn)form表單獲取輸入數(shù)據(jù)功能,涉及微信小程序針對form表單的事件綁定及數(shù)據(jù)獲取等相關(guān)操作技巧,需要的朋友可以參考下2017-11-11JS實現(xiàn)帶鼠標(biāo)效果的頭像及文章列表代碼
這篇文章主要介紹了JS實現(xiàn)帶鼠標(biāo)效果的頭像及文章列表代碼,涉及JavaScript響應(yīng)鼠標(biāo)事件動態(tài)切換頁面元素樣式的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09javascript函數(shù)定義的幾種區(qū)別小結(jié)
本篇文章主要是對javascript函數(shù)定義的幾種區(qū)別進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01javascript實現(xiàn)復(fù)制與粘貼操作實例
這篇文章主要介紹了javascript實現(xiàn)復(fù)制與粘貼操作,以實例形式講述了javascript實現(xiàn)復(fù)制與粘貼操作的實現(xiàn)方法,需要的朋友可以參考下2014-10-10