假设我有一个动物界面,我希望它具有一些通用属性,然后是猫或狗并具有相应的属性。
interface Dog {
dog: { sound: string; }
}
interface Cat {
cat: { lives: number; }
}
type CatOrDog = Cat | Dog;
interface Animal {
weight: number;
// index type of CatOrDog
}
所以我在想
interface Animal {
weight: number;
[K in keyof CatOrDog]: CatOrDog[K];
}
但是当我使用
[K:string]: type
以外的任何东西时,TypeScript 会非常生气
我想要实现的是
// Success
const dog = <Animal> {
weight: 5,
dog: {sound: "woof" }
}
// Error, lives doesn't exist on Dog
const errorAnimal = <Animal> {
weight: 5,
dog: {sound: "woof" },
cat: { lives: 9 }
}
另外,如果我想添加更多索引类型,可以吗?
像
Cat | Dog
这样的并集是 包容,这意味着如果某个东西是 Cat | Dog
或 Cat
或 两者,那么它就是
Dog
。 TypeScript 没有通用的“独占联合”运算符。 如果您的联合共享具有不同值的公共属性,则可以使用@MateuszKocz 建议的可区分联合。 否则,您可以构建自己的Xor
对象的类型函数:type ProhibitKeys<K extends keyof any> = { [P in K]?: never }
type Xor<T, U> = (T & ProhibitKeys<Exclude<keyof U, keyof T>>) |
(U & ProhibitKeys<Exclude<keyof T, keyof U>>);
然后您可以将
Animal
定义为
Cat
和 Dog
的互斥并集,相交以及所有
Animal
共有的附加属性:type Animal = Xor<Cat, Dog> & { weight: number };
现在你可以得到你想要的行为(类型注释优于类型断言,所以我在这里使用它们):
// Success
const dog: Animal = {
weight: 5,
dog: { sound: "woof" }
}
// Error, {lives: number} not assignable to undefined
const errorAnimal: Animal = {
weight: 5,
dog: { sound: "woof" },
cat: { lives: 9 }
}
将是您正在寻找的答案。
interface CommonAnimal {
weight: number
}
interface Dog extends CommonAnimal {
// This is the important part. `type` a tag used by TS to recognise this type.
type: 'dog'
sound: string
}
interface Cat extends CommonAnimal {
type: 'cat'
lives: number
}
type Animal = Dog | Cat
const dog: Animal = {
type: 'dog',
weight: 10,
sound: 'woof'
}
const cat: Animal = {
type: 'cat',
weight: 5,
lives: 9
}
const robot: Animal = {
type: 'robot' // error
}
这样您就能够将值保留在一个级别上,无需嵌套,同时满足 TS 的类型识别。