是否可以更改接口中声明的属性的类型?我不知道如何用文字解释这个,所以这里有一个简单的例子,说明我尝试做的事情并不起作用:
假设Movie
名称空间是在我无法控制的第三方库中定义的:
declare namespace Movie {
interface Character {
doSomething(): Promise<void>;
friend: Character;
}
}
现在在我的应用程序中,我希望我的角色成为Super,与超级朋友一起。所以我试着用自己的打字做到这一点:
declare namespace Movie {
interface Character {
doAnExtraThing(): Promise<void>; // OK to add a new method
nickname: string; // OK to add a new property
friend: SuperCharacter; // NOT OK to override type with a subtype
}
interface SuperCharacter extends Character {
doSomethingSuper(): Promise<void>;
}
}
但这并不起作用,因为TypeScript不允许我用friend
覆盖SuperCharacter
属性类型,即使定义为SuperCharacter
a Character
。 TypeScript抱怨此错误:
[ts] Subsequent variable declarations must have the same type. Variable
'friend' must be of type 'Character', but here has type 'SuperCharacter'.
我希望通过SuperCharacter
扩展原始的Character
界面,我不会遇到这个问题,但我确实如此。
希望能清楚我想要实现的目标。有办法吗?
答案 0 :(得分:0)
由于friend
已经是SuperCharacter
,因此没有理由明确指出SuperCharacter
可以是Character
。
所以你可以完全删除这个块:
界面角色{ 朋友:SuperCharacter; }
作为证明,我们可以远离接口,查看一个可以正确编译的类的实现:
class Character {
friend?: Character
constructor(friend?: Character) {
this.friend = friend
}
doSomething() {}
}
class SuperCharacter extends Character {
doSomethingSuper() {}
}
new Character(new Character())
new Character(new SuperCharacter())
new SuperCharacter(new Character())
new SuperCharacter(new SuperCharacter())
答案 1 :(得分:0)
您可以使用ts-toolbelt来实现。但是,您将没有type
:
import {O} from 'ts-toolbelt'
interface Character {
doSomething(): Promise<void>
friend: Character
}
interface SuperCharacter {
doSomethingSuper(): Promise<void>;
friend: SuperCharacter;
}
// SuperCharacter will override Character
type SuperMerge = O.Merge<SuperCharacter, Character>
// type SuperMerge = {
// doSomethingSuper: () => Promise<void>;
// friend: SuperCharacter;
// doSomething: () => Promise<void>;
// }
然后做
interface SuperMergedCharacter extends SuperMerge {
...
}
答案 2 :(得分:0)
我想出了两种解决方案:
您也可以使用联合类型和条件类型来完成pirix-gh的答案。
首先,我们将两种类型合并在一起:
type Merge<T, U> = T & U
然后我们可以使用条件类型来更改字段好友:
type ChangeProperty<T, K extends keyof T, U> = {
[Prop in keyof T]: Prop extends K ? U : T[Prop]
}
要产生所需的类型,请像这样使用它:
// From the namespace
interface Character {
doSomething(): Promise<void>;
friend: Character;
}
interface SuperCharacter extends Character {
doSomethingSuper(): Promise<void>;
}
type MyCharacter = ChangeProperty<Merge<SuperCharacter, SuperCharacter>, "friend", SuperCharacter>
使用这种方法,您不必向SuperCharacter添加字段,您可以重用friend
字段并将其类型更改为SuperCharacter
。
这是基于pirix-gh的答案,但没有库。还使用联合和条件类型。
type SuperMerge<T, U> = {
[K in keyof (T & U)]: K extends keyof T ? T[K] : K extends keyof U ? U[K] : never
}
类型SuperMerge
组合T和U,然后对其进行迭代,然后在T中使用该属性时,它将在T中使用该类型,否则将在U中使用该类型。永不发生。
您可以像这样使用它:
interface SuperCharacter extends Character {
doSomethingSuper(): Promise<void>;
friend: SuperCharacter // You need to this line in order to overwrite the default
}
使用此解决方案,您需要将具有新类型的外地朋友添加到界面中,以覆盖默认值。第一种类型包含所有前导属性,并将保留,因此如果第二种类型中有任何重复项,则第一种类型的信息将被覆盖。第二种类型的唯一功能是使用额外的属性扩展第一种类型。
type MyCharacter = SuperMerge<SuperCharacter, Character>
两个解决方案都输出相同的结果:
type MyCharacter = {
doSomethingSuper: () => Promise<void>;
doSomething: () => Promise<void>;
friend: SuperCharacter;
}
为了选择一个,我认为两者都有优点和缺点。当您要合并两种您无法控制的类型时,第一种解决方案很有用,但是您一次只能更改一种类型。您可以递归调用use ChangeType
来更改更多属性。
使用解决方案2时,您可以将新属性设置为新类型,并且具有相同名称的所有属性将被第一种类型所取代。
使用两种解决方案,您都可以合并自己的类型。