【问题标题】:Typescript: Change conflicting property name when extending interface打字稿:扩展接口时更改冲突的属性名称
【发布时间】:2019-09-27 13:05:48
【问题描述】:

有没有办法用打字稿做这样的事情,重命名属性名或其他什么?

interface Person {
    name: string
    age: number
}

interface Pet {
    age: string
}

interface Zoo extends Pet, Person {}

没有收到此错误:

接口“动物园”不能同时扩展类型“人”和“宠物” 'Person' 和 'Pet' 类型的命名属性 'age' 不相同.ts(2320)

[编辑]

我正在寻找获得此结果的方法:

interface Zoo {
    name: string
    age: number
}

【问题讨论】:

  • 您能否包括您期望Zoo 在结构上的样子?比如,如果你手动定义它,它的属性键和值类型是什么?
  • 顺便说一句,你不会得到programmatically renamed keys,所以任何重命名都会有点手动(例如,需要像interface RenameConflictingKeys {age: "personAge"}这样的类型)。
  • 为什么不直接使用有区别的联合呢?年龄:数字 |字符串;
  • @jcalz 刚刚编辑了我的问题

标签: typescript inheritance


【解决方案1】:

啊,所以你不是要重命名属性;只需从其中一个接口中删除任何冲突的属性。

这与object spread type operator 的想法非常相似,TypeScript 目前在类型级别没有将其作为语言的一部分。目前,类型系统将泛型扩展视为 intersection,这仅适用于非冲突属性。)在值/表达式级别,TypeScript 确实可以正确处理具体类型的扩展,因此您可以获得特定的 Zoo '通过说服类型系统你有一个Pet类型的值,一个Person类型的值,以及一个通过传播它们形成的值来寻找:

interface Person {
  name: string
  age: number
}

interface Pet {
  age: string
  trained: boolean // I added this because otherwise Zoo=Person which is weird
}

declare const person: Person; // pretend we have a person
declare const pet: Pet; // pretend we have a pet
const zoo = { ...pet, ...person }; // spread them into a new variable
type ZooType = typeof zoo; // get the type of that variable
// type ZooType = {
//   name: string;
//   age: number;
//   trained: boolean;
// } 
interface Zoo extends ZooType { }; // make it an interface because why not

如果你不想乱搞值(特别是如果你没有任何东西)并且你想纯粹在类型级别上做这件事,你可以用 @ 自己制作一个类似传播的类型运算符987654323@ 和 conditional 类型。一位语言设计者建议使用 implementationSpread<L,R> 来解决一些关于可选/只读/等属性的警告。

对于您的情况,由于您没有可选或只读属性,因此这里有一个更简单的实现,我将其称为 Merge<L, R>

type Merge<L, R> = R & Pick<L, Exclude<keyof L, keyof R>>;

你可以看到它的行为类似于上面的价值级传播:

interface Zoo extends Merge<Pet, Person> { };

declare const zoo: Zoo;
zoo.name; // string
zoo.age; // number
zoo.trained; // boolean

注意事项:如果agePerson 中是可选,那么Merge&lt;Pet, Person&gt; 将使ageZoo 中成为可选。这可能是你想要的,但传播不会那样做; {...pet, ...person} 总是有一个age,因为Pet 需要一个。在一个扩展中,Zoo["age"] 将类似于string | number,上面链接的Spread&lt;L, R&gt; 运算符将更正确地处理它。同样,Merge&lt;&gt; 和链接的 Spread&lt;&gt; 都不能保证使用 readonly 属性做“正确的事情”,特别是因为我不清楚正确的事情是什么。

好的,希望对您有所帮助。祝你好运!

【讨论】:

  • 嘿,我看了你的回答后有个问题。您说“交集仅适用于非冲突属性”,但const zoo = &lt;Person &amp; Pet&gt;{ ...pet, ...person } 没有错误
猜你喜欢
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 2013-03-26
  • 2021-08-14
  • 1970-01-01
  • 2019-01-20
  • 1970-01-01
相关资源
最近更新 更多