轉發 TypeScript基礎入門之高級類型的可辨識聯合(Discriminated Unions)編程
你能夠合併單例類型,聯合類型,類型保護和類型別名來建立一個叫作 可辨識聯合的高級模式,它也稱作 標籤聯合或 代數數據類型。 可辨識聯合在函數式編程頗有用處。 一些語言會自動地爲你辨識聯合;而TypeScript則基於已有的JavaScript模式。 它具備3個要素:函數式編程
1. 具備普通的單例類型屬性— 可辨識的特徵。
2. 一個類型別名包含了那些類型的聯合— 聯合。
3. 此屬性上的類型保護。函數
interface Interface1 { kind: 'interface1', property1: number, } interface Interface2 { kind: 'interface2', property2: number, property3: number, } interface Interface3 { kind: 'interface3', property4: number, property5: number, }
首先咱們聲明瞭將要聯合的接口。 每一個接口都有 kind屬性但有不一樣的字符串字面量類型。 kind屬性稱作 可辨識的特徵或 標籤。 其它的屬性則特定於各個接口。 注意,目前各個接口間是沒有聯繫的。 下面咱們把它們聯合到一塊兒:spa
type Type = Interface1 | Interface2 | Interface3;
如今咱們使用可辨識聯合:code
function getType(i: Type) { switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; } }
當沒有涵蓋全部可辨識聯合的變化時,咱們想讓編譯器能夠通知咱們。 好比,若是咱們添加了Interface4到Type,咱們同時還須要更新 area:blog
interface Interface4 { kind: 'interface4', property6: number, } type Type = Interface1 | Interface2 | Interface3 | Interface4; function getType(i: Type) { switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; } }
有兩種方式能夠實現。 首先是啓用 --strictNullChecks而且指定一個返回值類型:接口
function getType(i: Type): number { // error: returns number | undefined switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; } }
由於 switch沒有包涵全部狀況,因此TypeScript認爲這個函數有時候會返回 undefined。 若是你明確地指定了返回值類型爲 number,那麼你會看到一個錯誤,由於實際上返回值的類型爲 number | undefined。 然而,這種方法存在些微妙之處且 --strictNullChecks對舊代碼支持很差。第二種方法使用 never類型,編譯器用它來進行完整性檢查:ip
function assertNever(x: never): never { throw new Error("Unexpected object: " + x); } function getType(i: Type): number { // error: returns number | undefined switch (i.kind) { case "interface1": return i.property1 * i .property1; case "interface2": return i.property2 * i.property3; case "interface3": return i.property4 * i.property5; default: return assertNever(i); // error here if there are missing cases } }
這裏, assertNever檢查 s是否爲 never類型—即爲除去全部可能狀況後剩下的類型。 若是你忘記了某個case,那麼 s將具備一個真實的類型而且你會獲得一個錯誤。 這種方式須要你定義一個額外的函數,可是在你忘記某個case的時候也更加明顯。**Tips**
上面的代碼是根據官網的邏輯寫的,奇怪的是最後一步竟然在編譯的時候報錯了。報錯信息以下字符串
$ tsc src/advanced_types_4.ts src/advanced_types_4.ts:38:26 - error TS2345: Argument of type 'Interface4' is not assignable to parameter of type 'never'. 38 return assertNever(i); // error here if there are missing cases ~