【问题标题】:How to overwrite property for intersection type in Typescript?如何在 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),但找不到我需要的东西。感谢您提前提供任何答案!

【问题讨论】:

    标签: typescript types .d.ts


    【解决方案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)”。
    猜你喜欢
    • 2017-08-22
    • 2018-09-13
    • 2013-04-05
    • 2012-10-17
    • 2018-08-18
    • 2017-05-08
    • 2018-08-31
    • 2016-11-06
    • 2021-12-30
    相关资源
    最近更新 更多