如果我通过子类实例调用基类的update
方法,则该方法有效,但是如果我在子类方法中进行操作,则会出现此错误:
类型'{prop:number; }”不能分配给“ Partial
”类型的参数。
class Base {
update(modifier: Partial<this>) {
Object.keys(modifier).forEach(key => {
this[key] = modifier[key];
});
}
}
class Sub extends Base {
prop;
job() {
// Error: Argument of type '{ prop: number; }' is not assignable
// to parameter of type 'Partial<this>'.ts(2345)
this.update({ prop: 1 });
}
}
new Sub().update({ prop: 1 }); // but it works here!
Live on the TypeScript playground。
我该如何解决?
答案 0 :(得分:2)
多态this
不是常规类型,实际上是类的类型参数。由编译器管理的隐藏类型参数,但是仍然是类型参数。由于this
只是一个类型参数,并且具有扩展当前类的约束(类似class Base<This extends Base> {}
之类),因此在类内部并不完全知道。
关于SO的问题很多,为什么我们不能为泛型类型参数的变量赋值,或者为什么条件/映射类型不能与泛型类型参数一起工作。例如,这也不起作用:
function partial<T extends { prop: number }>(): Partial<T> {
return { prop: 1 }
}
这类一般问题无法解决的原因是,我们仅知道必须实现的最小接口T
,而我们不完全了解T
。考虑以下示例:
function getObj<T extends { prop: { name: string } }>(): Partial<T> {
return { prop: { name: "" } }// still an error
}
var r = getObj<{ prop: { name: string, lastName: string}}>();
r.prop!.lastName // r should have last name, where is my lastName ?!
prop
中的 T
被约束为拥有name
,但它可以具有更多道具,如下面的有效呼叫所示。我们希望r
尊重传入的T
,但实际上返回的值对于给定的T
无效。为防止此类意外,打字稿通常不允许我们在涉及类型参数的地方分配具体值(因为我们实际上并不知道类型参数的最终形状)
回到多态this
,上述推理成立,this
是一个类型参数,尚不完全清楚,因此无法为其分配对象文字,因为我们不知道this
的最终形状。
请考虑与上述示例类似的示例:
class Base {
update(modifier: Partial<this>) {
Object.keys(modifier).forEach(key => {
this[key] = modifier[key];
});
}
}
class Sub extends Base {
prop! :{
name: string
};
job() {
this.update({ prop: { name: string } }); // not valid for any class, example below
}
}
class Sub2 extends Sub {
prop! : {
name: string
lastName: string
};
}
我选择用对象类型替换number
,因为它更容易理解问题,但是number
可能会发生类似的类型问题。 Number可以是派生类中的数字文字类型。
在类多态this
之外是已知的(就像在对泛型函数的调用中一样),这就是在类外调用成功的原因。
new Sub().update({ prop: 1 }); // this is Sub for this call so Partial<Sub> is { prop?: number }
在多态类this
内可以是Sub
或Sub
的任何派生类。在this
外,仅折叠到调用了该函数的特定类型引用(这也不是100%合理的,但这是折中的作法)
解决此问题的唯一方法是要么使用类型断言,然后接受这不是类型安全的。或使用代表类道具的第二个类型参数,尽管这将意味着您的继承层次结构只能具有一个可以添加新道具的级别。
class Base<TProps> {
update(modifier: Partial<TProps>) {
Object.assign(this, modifier);
}
}
class Sub extends Base<Sub> {
prop: number;
job() {
this.update({ prop: 1 });
}
}