TypeScript 可辨識聯合型別

admin發表於2019-05-18

可以合併字串字面量型別,聯合型別,型別保護和型別別名來建立一個叫做可辨識聯合型別。

TypeScript基於已有的JavaScript模式, 具有3個要素:

(1).具有普通字串字面量屬性—可辨識的特徵。

(2).一個型別別名包含型別的聯合—聯合。

(3).此屬性上的型別保護。

程式碼例項:

[typescript] 純文字檢視 複製程式碼
interface Square {
  kind: "square";
  size: number;
}
interface Rectangle {
  kind: "rectangle";
  width: number;
  height: number;
}
interface Circle {
  kind: "circle";
  radius: number;
}

宣告三個介面,每個介面都有kind屬性,但有不同的字串字面量型別, kind屬性稱做可辨識的特徵或標籤,其它的屬性則特定於各個介面。

[typescript] 純文字檢視 複製程式碼
type Shape = Square | Rectangle | Circle;

將三個型別聯合起來,組成一個聯合型別,並重新命名。

[typescript] 純文字檢視 複製程式碼
function area(shape: Shape) {
  switch (shape.kind) {
    case "square": return shape.size * shape.size;
    case "rectangle": return shape.height * shape.width;
    case "circle": return Math.PI * shape.radius ** 2;
  }
}

通過型別保護,實現辨識聯合型別中的每一個型別;關於型別保護參閱TypeScript 型別保護一章節。

完整性檢查:

當沒有涵蓋所有可辨識聯合的變化時,想讓編譯器可以通知我們。

例如,如果新增Triangle到Shape,同時還需要更新area:

[typescript] 純文字檢視 複製程式碼
type Shape = Square | Rectangle | Circle | Triangle;
function area(s: Shape) {
  switch (s.kind) {
    case "square": return s.size * s.size;
    case "rectangle": return s.height * s.width;
    case "circle": return Math.PI * s.radius ** 2;
  }
  // 應該報錯,因為沒有處理Triangle
}

有兩種方式可以實現。 首先是啟用--strictNullChecks並且指定一個返回值型別:

[typescript] 純文字檢視 複製程式碼
function area(s: Shape): number { // error: returns number | undefined
  switch (s.kind) {
    case "square": return s.size * s.size;
    case "rectangle": return s.height * s.width;
    case "circle": return Math.PI * s.radius ** 2;
  }
}

因為switch沒有包涵所有情況,所以TypeScript認為這個函式有時候會返回undefined。

如果明確地指定了返回值型別為number,那麼看到一個錯誤,因為實際上返回值的型別為number | undefined。 然而,這種方法存在些微妙之處且--strictNullChecks對舊程式碼支援不好。

第二種方法使用never型別,編譯器用它來進行完整性檢查:

[typescript] 純文字檢視 複製程式碼
function assertNever(x: never): never {
  throw new Error("Unexpected object: " + x);
}
function area(shape: Shape) {
  switch (shape.kind) {
    case "square": return shape.size * shape.size;
    case "rectangle": return shape.height * shape.width;
    case "circle": return Math.PI * shape.radius ** 2;
    default: return assertNever(shape); // error here if there are missing cases
  }
}

assertNever檢查shape是否為never型別—即為除去所有可能情況後剩下的型別。

如果忘記了某個case,那麼shape將具有一個真實的型別並且你會得到一個錯誤。 這種方式需要定義一個額外的函式,但是在你忘記某個case的時候也更加明顯。

相關文章