簡體   English   中英

類型的並集和交集

[英]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" };

TypeScriptPET2編譯器錯誤說我需要添加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 ,它必須可以分配到兩個CatDog 因為對象類型是開放的,所以沒有說Cat不能有barksbites屬性。 並沒有說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必須只分配給任何CatDog 如果您為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM