在接口上定义索引类型的选择

问题描述 投票:0回答:2

假设我有一个动物界面,我希望它具有一些通用属性,然后是猫或狗并具有相应的属性。

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

另外,如果我想添加更多索引类型,可以吗?

javascript typescript
2个回答
1
投票

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



0
投票
标记的联合

将是您正在寻找的答案。 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 的类型识别。

© www.soinside.com 2019 - 2024. All rights reserved.