數(shù)字字面量類型
TypeScript還具有數(shù)字字面量類型。
function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
? ? // ...
}
我們很少直接這樣使用,但它們可以用在縮小范圍調(diào)試bug的時(shí)候:
function foo(x: number) {
? ? if (x !== 1 || x !== 2) {
? ? ? ? //? ? ? ? ~~~~~~~
? ? ? ? // Operator '!==' cannot be applied to types '1' and '2'.
? ? }
}
換句話說嫉称,當(dāng) x與 2進(jìn)行比較的時(shí)候在跳,它的值必須為 1柬甥,這就意味著上面的比較檢查是非法的浪慌。
枚舉成員類型
如我們在 枚舉一節(jié)里提到的,當(dāng)每個(gè)枚舉成員都是用字面量初始化的時(shí)候枚舉成員是具有類型的辽话。
在我們談及“單例類型”的時(shí)候,多數(shù)是指枚舉成員類型和數(shù)字/字符串字面量類型卫病,盡管大多數(shù)用戶會互換使用“單例類型”和“字面量類型”油啤。
可辨識聯(lián)合(Discriminated Unions)
你可以合并單例類型,聯(lián)合類型蟀苛,類型保護(hù)和類型別名來創(chuàng)建一個(gè)叫做 可辨識聯(lián)合的高級模式益咬,它也稱做 標(biāo)簽聯(lián)合或 代數(shù)數(shù)據(jù)類型。 可辨識聯(lián)合在函數(shù)式編程很有用處帜平。 一些語言會自動(dòng)地為你辨識聯(lián)合幽告;而TypeScript則基于已有的JavaScript模式梅鹦。 它具有3個(gè)要素:
具有普通的單例類型屬性— 可辨識的特征。
一個(gè)類型別名包含了那些類型的聯(lián)合— 聯(lián)合冗锁。
此屬性上的類型保護(hù)齐唆。
interface Square {
? ? kind: "square";
? ? size: number;
}
interface Rectangle {
? ? kind: "rectangle";
? ? width: number;
? ? height: number;
}
interface Circle {
? ? kind: "circle";
? ? radius: number;
}
首先我們聲明了將要聯(lián)合的接口。 每個(gè)接口都有 kind屬性但有不同的字符串字面量類型冻河。 kind屬性稱做 可辨識的特征或 標(biāo)簽箍邮。 其它的屬性則特定于各個(gè)接口。 注意叨叙,目前各個(gè)接口間是沒有聯(lián)系的溉委。 下面我們把它們聯(lián)合到一起:
type Shape = Square | Rectangle | Circle;
現(xiàn)在我們使用可辨識聯(lián)合:
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;
? ? }
}
完整性檢查
當(dāng)沒有涵蓋所有可辨識聯(lián)合的變化時(shí)唉俗,我們想讓編譯器可以通知我們。 比如,如果我們添加了 Triangle到 Shape熄求,我們同時(shí)還需要更新 area:
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;
? ? }
? ? // should error here - we didn't handle case "triangle"
}
有兩種方式可以實(shí)現(xiàn)。 首先是啟用 --strictNullChecks并且指定一個(gè)返回值類型:
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;
? ? }
}
因?yàn)?switch沒有包涵所有情況栓辜,所以TypeScript認(rèn)為這個(gè)函數(shù)有時(shí)候會返回 undefined卜范。 如果你明確地指定了返回值類型為 number,那么你會看到一個(gè)錯(cuò)誤行楞,因?yàn)閷?shí)際上返回值的類型為 number | undefined攒暇。 然而,這種方法存在些微妙之處且 --strictNullChecks對舊代碼支持不好子房。
第二種方法使用 never類型形用,編譯器用它來進(jìn)行完整性檢查:
function assertNever(x: never): never {
? ? throw new Error("Unexpected object: " + x);
}
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;
? ? ? ? default: return assertNever(s); // error here if there are missing cases
? ? }
}
這里, assertNever檢查 s是否為 never類型—即為除去所有可能情況后剩下的類型证杭。 如果你忘記了某個(gè)case田度,那么 s將具有一個(gè)真實(shí)的類型并且你會得到一個(gè)錯(cuò)誤。 這種方式需要你定義一個(gè)額外的函數(shù)解愤,但是在你忘記某個(gè)case的時(shí)候也更加明顯镇饺。