类型的并集和交集

14 浏览
0 Comments

类型的并集和交集

为了练习,我写了这段代码:

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" };

但是pet2上的编译器报错,说我需要在对象中添加purrs属性。

但是CatAndDog不是这两个类型的交集吗,只有一个name属性吗?

0
0 Comments

在学习TypeScript时,人们经常混淆交叉类型和联合类型,原因可能是对象类型在键的类型上是逆变的,所以对象类型的交叉类型是其键的联合类型,反之亦然。这种混淆的原因可能是因为人们将对象类型等同于其声明属性的集合。实际上,应该将类型视为允许的值的集合。联合类型和交叉类型应该被看作是可分配给这些类型的值集合的并集和交集。

对于字面量类型"foo"来说,集合只有一个元素:{"foo"}。对于类型string来说,它是所有JavaScript字符串的(实际上)无穷大的集合:{x | typeof x === "string"}。对于像{y: 0}这样的对象类型,它也是一个无穷大的集合:{x | x.y === 0},也就是说,所有具有y属性值等于0的JavaScript对象的集合。

另一个可能引起混淆的原因是TypeScript中的对象类型是开放的,而不是封闭的或精确的。对象类型的定义显示了必须存在的属性,但它们不包括不能存在的属性。一个对象可以比其类型定义中提到的属性多出更多的属性。由于对象类型是开放的,这意味着可赋值的值的集合比你可能想象的要大。例如,{a: 0}和{b: 1}这样的两个对象类型实际上有重叠;例如,值{a: 0, b: 1, c: 2, d: 3}可以分配给它们中的任何一个。

现在我们来思考一下交叉类型(&)和联合类型(|):

如果我有一个类型为Cat & Dog的对象,它必须同时可以分配给Cat和Dog。由于对象类型是开放的,没有任何东西表示Cat不能有barks或bites属性,也没有任何东西表示Dog不能有purrs属性。因此,如果你有一个既是Cat又是Dog的东西,它必须具有两种类型的所有属性。

另一方面,类型为Cat | Dog的对象只能分配给Cat或Dog中的一个。如果将一个值分配给类型为Cat | Dog的变量,它需要至少是其中一个。

如果我有一个类型为Cat | Dog的对象,并且我还没有检查它以确定是Cat还是Dog,我只能访问它的name属性,因为这是我唯一确定会存在的属性。一个Cat | Dog可能同时具有两种类型的一些属性,如你初始化pet1所示,但你不能保证它。

交叉类型和联合类型在类型的属性集合和允许的值集合之间有所不同。交叉类型要求具有所有类型的属性,而联合类型只要求具有其中一个类型的属性。

(代码见原文)

0