[英]Union and Intersection of types
為了練習,我寫了這段代碼:
type Cat = {name: string, purrs: boolean};
type Dog = {name: string, barks: boolean, bites: boolean};
type CatOrDogOrBoth = Cat | Dog;
type CatAndDog = Cat & Dog;
let pet1 : CatOrDogOrBoth = {name: "pooky", purrs: true, bites: false};
let pet2 : CatAndDog = {name: "Timmy" };
但TypeScript
上PET2編譯器錯誤說我需要添加purrs
到我的對象。 但是CatAndDog
不是這兩種類型的交集,所以只有name屬性嗎?
你有向后的交集和聯合,出於某種原因,當人們學習 TypeScript 時,這並不少見。 造成這種混淆的一個可能原因是對象類型的鍵類型是逆變的,因此對象類型的交集具有其鍵的並集,反之亦然。 也就是說, keyof (Cat & Dog)
和(keyof Cat) | (keyof Dog)
是一樣的(keyof Cat) | (keyof Dog)
(keyof Cat) | (keyof Dog)
和keyof (Cat | Dog)
與(keyof Cat) & (keyof Dog)
:
type KeyExploration = {
keysOfIntersection: keyof (Cat & Dog) // "name" | "purrs" | "barks" | "bites"
unionOfKeys: keyof Cat | keyof Dog // "name" | "purrs" | "barks" | "bites"
keysOfUnion: keyof (Cat | Dog) // "name"
intersectionOfKeys: (keyof Cat) & (keyof Dog) // "name"
}
由於這種逆變,如果您對對象類型的概念等同於其聲明的屬性集,您將混淆交集和並集。 相反,您應該將類型視為一組允許的值(有關更多信息,請參閱此答案)。 並且您應該將類型的聯合和交集視為可分配給這些類型的值集的聯合和交集。
對於像"foo"
這樣的文字類型,集合只有一個元素:{ "foo"
}。 對於像string
這樣的類型,它是所有 JavaScript 字符串的(實際上)無限集:{ x
| typeof x === "string"
} (使用set builder 符號)。 對於像{y: 0}
這樣的對象類型,它也是一個無限集:{ x
| xy === 0
}... 即所有y
屬性完全等於0
JavaScript 對象的集合。
——
另一個可能的混淆來源是 TypeScript 中的對象類型是開放的,而不是封閉的或精確的(請參閱microsoft/TypeScript#12936以獲取精確類型的請求)。
對象類型定義顯示了哪些屬性必須存在,但它們並沒有討論哪些屬性不能存在。 一個對象可能具有比其類型定義中提到的更多的屬性:
interface Garfield extends Cat {
hatesMondays: true,
eatsLasagna: true,
}
declare const garfield: Garfield;
const garfieldAsACat: Cat = garfield; // okay
(由於存在多余的屬性檢查,這有點復雜,它將“新鮮”對象文字視為精確類型。但此類檢查是例外而不是規則。)
由於對象類型是開放的,這意味着可分配值的集合比您想象的要大。 {a: 0}
和{b: 1}
等兩種對象類型實際上有顯着的重疊; 例如,值{a: 0, b: 1, c: 2, d: 3}
可以分配給它們兩個。
現在讓我們考慮交集 ( &
) 和聯合 ( |
):
如果我有類型的對象Cat & Dog
,它必須可以分配到兩個Cat
和Dog
。 因為對象類型是開放的,所以沒有說Cat
不能有barks
或bites
屬性。 並沒有說Dog
不能有purrs
屬性。 因此,如果您有既是Cat
又是Dog
,它必須具有這兩種類型的所有屬性。
let okay1: CatAndDog =
{ name: "CatDog", purrs: true, bites: true, barks: true }; // Cat and Dog
pet2
失敗,因為它既不是Cat
也不是Dog
:
let pet2: CatAndDog = { name: "Timmy" }; // neither Cat nor Dog
另一方面,類型為Cat | Dog
的對象 Cat | Dog
必須只分配給任何Cat
或Dog
。 如果您為Cat | Dog
類型的變量賦值 Cat | Dog
它至少需要是以下之一:
let okay1: CatOrDogOrBoth =
{ name: "Sylvester", purrs: false }; // Cat
let okay2: CatOrDogOrBoth =
{ name: "Odie", barks: true, bites: false }; // Dog
您的pet1
是可以接受的,因為它是Cat
。 它有一個額外的bites
屬性,這很好(並且不會被過多的屬性檢查所捕獲,盡管有些人認為它應該(參見microsoft/TypeScript#20863 ):
let pet1: CatOrDogOrBoth =
{ name: "pooky", purrs: true, bites: false }; // Cat with bites:false
如果我有一個Cat | Dog
類型的對象 Cat | Dog
和我還沒有檢查它以查看它是Cat
還是Dog
哪一個,我可以訪問的唯一安全屬性是它的name
,因為這是我確定會出現的唯一屬性。 有可能是一只Cat | Dog
Cat | Dog
將具有兩種類型的一些屬性,正如您在pet1
的初始化中所顯示的pet1
,但您不能保證這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.