TypeScript基礎入門之高級類型的可辨識聯合(Discriminated Unions)

轉發 TypeScript基礎入門之高級類型的可辨識聯合(Discriminated Unions)編程

高級類型

可辨識聯合(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
                            ~
相關文章
相關標籤/搜索