如何在 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 | CT extends U ? X : Y 的实例化被解析为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)”。

以上是关于如何在 Typescript 中覆盖交集类型的属性?的主要内容,如果未能解决你的问题,请参考以下文章

typescript 关于class属性类型定义被属性默认值覆盖的问题及解决方式

TypeScript:覆盖和修复包的类型定义

如何覆盖 TypeScript UMD 全局类型定义?

TypeScript 中的交集和合并类型 ` foo & bar ` 和 ` foo, bar ` 之间的区别

如何覆盖这个 ESLint TypeScript 规则?

覆盖 MuiContainer 类时如何摆脱 Typescript 类型错误?