Typescript 类型的并集和交集
为了练习,我编写了以下代码:Typescript 类型的并集和交集,typescript,Typescript,为了练习,我编写了以下代码: 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: fa
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
不是这两种类型的交叉点,所以只是一个名称属性吗 交集和并集是向后的,这在人们学习打字脚本时并不少见。造成这种混淆的一个可能原因是对象类型位于其键的类型中,因此对象类型的交集具有其键的并集,反之亦然。也就是说,keyof(猫和狗)
与(keyof猫)|(keyof狗)
相同,keyof(猫和狗)
与(keyof猫)和(keyof狗)
相同:
由于这种矛盾,如果对象类型的概念与其声明的属性集相等,则会混淆交点和并集。相反,您应该将类型视为一组允许的值(有关更多信息,请参阅)。您应该将类型的并集和交集视为可分配给这些类型的值集的并集和交集
对于类似的“foo”
,集合有一个元素:{“foo”
}。对于像string
这样的类型,它实际上是所有JavaScript字符串的无限集:{x
|typeof x===“string”
}(使用)。对于像{y:0}
这样的对象类型,它也是一个无限集:{x
|x.y==0
}。。。也就是说,y
属性完全等于0
的所有JavaScript对象的集合
--
另一个可能引起混淆的原因是TypeScript中的对象类型是开放的,而不是封闭的或精确的(有关精确类型的请求,请参阅)
对象类型定义显示了哪些属性必须存在,但它们没有说明哪些属性不能存在。对象的属性可能多于其类型定义中提到的属性:
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
属性。也没有任何东西说一只狗不能有呼噜呼噜的特性。因此,如果您有同时是猫和狗的东西,那么它必须具有这两种类型的所有属性
而pet2
失败,因为它既不是Cat
也不是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"
}
let pet2: CatAndDog = { name: "Timmy" }; // neither Cat nor 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
属性,这很好(并且不会被多余的属性检查所捕获,尽管有些人认为它应该(参见):
如果我有一个类型为Cat | Dog
的对象,但我还没有检查它,以确定它是Cat
还是Dog
中的哪一个,那么我唯一可以访问的安全属性就是它的名称,因为这是我知道肯定会存在的唯一属性。Cat | Dog
可能会有一些适当的属性这两种类型的关系,正如您对pet1
的初始化所示,但您不能保证这一点
你有向后的交集和并集,这在人们学习TypeScript时并不少见。造成这种混乱的一个可能原因是对象类型在其键的类型中,因此对象类型的交集有其键的并集,反之亦然。也就是说,keyof(Cat&Dog)
与(猫的钥匙)|(狗的钥匙)
相同,keyof(猫的狗)
与(猫的钥匙)&(狗的钥匙)
相同:
由于这种矛盾,如果对象类型的概念与其声明的属性集相等,则会混淆交集和并集。相反,应将类型视为一组允许的值(有关详细信息,请参阅)。您应该将类型的并集和交集视为可分配给这些类型的值集的并集和交集
对于类似的“foo”
,集合只有一个元素:{“foo”
}。对于类似字符串的类型,它是(实际上)所有JavaScript字符串的无限集合:{x
|typeof x==“string”
}(使用)。对于{y:0}
等对象类型,它也是无限集合:{x
|x.y==0
}…也就是说,y
属性完全等于0
的所有JavaScript对象的集合
--
另一个可能引起混淆的原因是TypeScript中的对象类型是开放的,而不是封闭的或精确的(有关精确类型的请求,请参阅)
对象类型定义显示了哪些属性必须存在,但它们没有说明哪些属性不能存在。对象的属性可能比其类型定义中提到的多:
interface Garfield extends Cat {
hatesMondays: true,
eatsLasagna: true,
}
declare const garfield: Garfield;
const garfieldAsACat: Cat = garfield; // okay
(由于存在,这有点复杂,它将“新鲜”对象文本视为
let pet1: CatOrDogOrBoth =
{ name: "pooky", purrs: true, bites: false }; // Cat with bites:false