如何在 Typescript 中为交集类型覆盖 属性?

How to overwrite property for intersection type in Typescript?

假设我有这些类型:

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 线程(例如 ,它是针对接口的),但找不到我需要的东西。感谢您提前回答!

如果你真的想坚持类型而不是接口,你可以使用泛型来避免重复:

type BaseAnimalParams<T extends BaseAnimal> = Omit<T, 'owner'> & {
    owner: string;
}

type AnimalParams = BaseAnimalParams<Dog> | BaseAnimalParams<Cat>;

您可以使用 distributive conditional type:

而不是手动省略每种类型的 owner 属性
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; })

这是由于联合类型的自动分配

Instantiation of T extends U ? X : Y with the type argument A | B | C for T is resolved as (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>>;