如何在 Typescript 中覆盖交集类型的属性?
Posted
技术标签:
【中文标题】如何在 Typescript 中覆盖交集类型的属性?【英文标题】:How to overwrite property for intersection type in Typescript? 【发布时间】:2020-11-05 18:14:49 【问题描述】:假设我有这些类型:
type BaseAnimal =
species: string
owner: boolean
type Cat = BaseAnimal &
species: 'cat'
hasTail: boolean
type Dog = BaseAnimal &
species: 'dog'
likesWalks: boolean
type Animal = Cat | Dog
我想创建一个名为AnimalParams
的类型,它与Animal
相同除了owner
属性是一个字符串。
以下任何一项我都做不到。
// This seems to keep the owner property from Animal instead of overwriting
// So it raises an error if I try to specify owner as a string
type AnimalParams = Animal &
owner: string
// This strips away properties unique to Cat or Dog
// So it raises an error if I try to specify hasTail or likesWalks
type AnimalParams = Omit<Animal, 'owner'> &
owner: string
现在,我能想到的唯一解决方法是执行以下操作,但这似乎是不必要的重复。有没有更干净、更简洁的方式?
type CatParams = Omit<Cat, 'owner'> &
owner: string
type DogParams = Omit<Dog, 'owner'> &
owner: string
type AnimalParams = CatParams | DogParams
我阅读了一些关于实用程序类型的 SO 线程(例如用于接口的 Overriding interface property type defined in Typescript d.ts file),但找不到我需要的东西。感谢您提前提供任何答案!
【问题讨论】:
【参考方案1】:如果你真的想坚持类型而不是接口,你可以使用泛型来避免重复:
type BaseAnimalParams<T extends BaseAnimal> = Omit<T, 'owner'> &
owner: string;
type AnimalParams = BaseAnimalParams<Dog> | BaseAnimalParams<Cat>;
【讨论】:
谢谢!我接受另一个答案,因为它更干燥(避免手动创建另一个联合类型),但你也有我的支持。 是的,完全同意。没想过使用分布式条件类型。【参考方案2】:您可以使用distributive conditional type,而不是手动省略每种类型的owner
prop:
type OmitOwner<T = Animal> = T extends BaseAnimal ? Omit<T, 'owner'> : never;
type AnimalParams = OmitOwner &
owner: string
;
相当于:
(Omit<Cat, 'owner'> & owner: string; )
| (Omit<Dog, 'owner'> & owner: string; )
这是由于联合类型的自动分配
使用
T
的类型参数A | B | C
实例化T extends U ? X : Y
被解析为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
Playground
为什么最初的尝试不起作用?
keyof
union 在 union 中产生类型键的交集,所以
type AnimalKeys = keyof Animal // is "species" | "owner"
Omit
的实现是:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
【讨论】:
这令人大开眼界。非常感谢!对于任何引用此问题的人 - 我建议您也阅读链接的文档。基本上,“T
的类型参数为A | B | C
的T extends U ? X : Y
的实例化被解析为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
”。以上是关于如何在 Typescript 中覆盖交集类型的属性?的主要内容,如果未能解决你的问题,请参考以下文章
typescript 关于class属性类型定义被属性默认值覆盖的问题及解决方式