让我们说我有一个Animal接口,我希望它具有一些常规属性,然后成为猫或狗并具有相应的属性。
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
我想要实现的是
// 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 }
}
此外,如果我想添加更多的索引类型,那有可能吗?
答案 0 :(得分:1)
诸如Cat | Dog
之类的联盟为inclusive,这意味着某事物如果是Cat | Dog
或Cat
或两者都是Dog
。 TypeScript没有通用的exclusive union运算符。如果您的工会拥有具有不同值的共同财产,则可以使用@MateuszKocz建议的有区别的工会。否则,您可以build your own 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
,intersected的排他联合,并具有所有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 }
}
希望有所帮助;祝你好运!
答案 1 :(得分:0)
如果您愿意稍微更改代码,那么tagged unions将是您想要的答案。
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的类型识别的同时,将值保持在一个级别而无需嵌套。