[英]Discriminate union types
為什么我不能僅通過成員 x 是字符串而不是數字的信息來區分該聯合? 為什么我必須使用文字類型?
type A = { x: string ; y: string }
type B = { x: number ; y: number }
type C = A | B
function fn(p: C) {
if (typeof p.x === 'string') {
// Typescript is unable to infer p as A
// Typescript infer p.y as (string | number), why not just string ?
}
// Typescript is capable of inferring p.x as number - good
// But cannot infer p.y as number, why ?
}
對我來說,用這樣的類型來調用函數是不可能的 {x:string,y:number},那么為什么 typescript 假設這是可能的呢?
不是直接解釋為什么 Typescript 編譯器不能通過typeof
區分聯合類型,但手冊說:
共有三種成分:
- 具有共同的單例類型屬性的類型- 判別式。
- 一個接受這些類型聯合的類型別名——聯合。
- 共同財產上的類型保護。
因此,為了縮小正確的類型,需要單例類型屬性。 我會使用自定義類型保護:
type A = { x: string ; y: string }
type B = { x: number ; y: number }
type C = A | B
function isA(obj: C): obj is A {
return typeof obj.x === 'string';
}
function isB(obj: C): obj is B {
return typeof obj.x === 'number';
}
function fn(p: C) {
if (isA(p)) {
// ...
}
if (isB(p)) {
// ...
}
}
如果您可以在類中表達類型,那么我建議您這樣做:
class A { x: string; y: string; }
class B { x: number; y: number }
type C = A | B
function fn(p: C) {
if (p instanceof A) {
// { x: string; y: string; }
}
if (p instanceof B) {
// { x: number; y: number }
}
}
在這種情況下,您可以使用instanceof魔法。 這是最好的方法。 否則,您需要檢查所有屬性,因為關鍵字類型僅向源添加定義。
雖然這個問題是一年多前提出的。 我希望我的演示可以幫助發現這個問題的人。
以下解釋引用自“Programming Typescript”,並進行了一些修改以適合此處的示例代碼:
當
isAorB
接受“AorB”類型的參數時,這並不意味着我們必須傳遞A
或B
—— 事實上,我們可以傳遞A | B
類型的參數A | B
A | B
type A = { x: string ; y: string };
type B = { x: number; y: number };
type AorB = A | B;
const c = { x: "str", y: "str" };
function isAorB(p: AorB) {
if (typeof p.x === "number") {
const alsoCx = p.x; // number
const alsoCy = p.y; // string | number
return;
}
const alsoCx = p.x; // string
const alsoCy = p.y; // string | number
}
isAorB(c);
我承認我在盯着這段很長一段時間后仍然不明白其中的邏輯。
但是,我嘗試了很多類型的組合,並認為 Typescript 假設以下也是可能的輸入:
// The above c really satisfy type C here.
type C = { x: string | number ; y: string | number };
盡管在不知道 Typescript 如何解析上述邏輯的情況下它甚至可能不是答案。 現在上述行為對我來說看起來並不奇怪。
注意:如果你將c
定義為const c: C = ...
,你實際上會在調用isAorB(c)
時遇到錯誤
既然你提到了“文字類型”,我假設你知道一個簡單的解決方案是在聯合對象的所有公共鍵中添加一個“唯一文字”值來區分它們。 這是給那些不知道的人的簡短示例(因為還沒有人發布帶有這種解決方案的示例)。
使用 Unique Literal Tag解決歧義:
// Notice "z" satisfy the condition of being a unique literal.
type A = { z: "A", x: string ; y: string };
type B = { z: "B", x: number; y: number };
type AorB = A | B;
function isAorB(p: AorB ) {
if (p.z === "A") {
const onlyAx = p.x; // string
const onlyAy = p.y; // string
return;
}
const onlyBx = p.x; // number
const onlyBy = p.y; // number
}
請注意,現在調用isAorB(c)
時,具有以下形式的c
將導致錯誤。
const c = { x: "str", y: "str", z: "A" };
你可以做這樣的事情來確保z
const c = { x: "str", y: "str", z: "A" as "A" };
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.