typescript 告诉我 this 可以分配给类型 T,但是 T 可以用约束的不同子类型来实例化 MainModel
class HelperClass {
public static someHelp<T extends MainModel<T>>(key: keyof T, instance: T): T[keyof T] {
return instance[key];
}
}
abstract class MainModel<T extends MainModel<T>> {
someProperty: string = "";
someMethod(key: keyof T) {
// some code
return HelperClass.someHelp(key, this); // <- error here
}
}
class FirstModel extends MainModel<FirstModel> {}
const firstModel = new FirstModel();
firstModel.someMethod("someProperty");
我不明白错误消息,是我做错了什么还是我的代码不完整?
如果我的子类型是通用的,“可以用不同的子类型实例化”是什么意思?
您在 class MainModel<T extends MainModel<T>>
中使用了
递归有界泛型,大概是为了使 generic 类型参数
T
可以用作类内 this
类型的同义词。不幸的是,它不是同义词,而是“约束”。也就是说,T
可能与 MainModel<T>
不同;你所知道的是T
是MainModel<T>
的一些子类型。 这会导致类似的可能性class FirstModel extends MainModel<FirstModel> {
x = 1;
}
class Oops extends MainModel<FirstModel> { }
new Oops().someMethod("x").valueOf(); //allowed to compile, but runtime error 💥
其中
FirstModel
实际上比
MainModel<FirstModel>
更具体(因为它有一个额外的 x
属性),因此您可以编写一个类 Oops
扩展 MainModel<FirstModel>
而不扩展 FirstModel
。 因此,Oops
有一个接受someMethod()
作为输入的"x"
,并且someMethod()
的实现将尝试访问x
的this
属性,并且你得到undefined
,不是
T[keyof T]
。 哎呀。这可能不太可能发生,但 TypeScript 没有意识到这一点,所以它会抱怨。如果您确信这不会成为问题,您可以随时断言:
someMethod(key: keyof T) {
return HelperClass.someHelp(key, this as MainModel<T> as T);
}
其中类型断言是您确认实现不安全的可能性的方式。
在其他一些语言中,尤其是 Java 和(至少历史上)C++,递归有界泛型(称为 F 界多态性或
)是最接近捕获 this
类型的语言。
this
类型
可以更好地满足该用例。名为
this
的类型是自动
this
的类型,这样潜在的子类将具有更具体的 this
类型。 它是隐式泛型的,并且受限于当前类,因此它与具有名为 class F<this extends F<this>> {}
的类型参数的 this
非常相似,但是您没有机会在类之外使用像 FirstModel
这样奇怪的类型 argument 来指定它对于Oops
。如果我改用多态this
,你的问题就会消失(并且代码看起来更简单,因为现在隐含了泛型,你不需要到处携带它):
abstract class MainModel {
someProperty: string = "";
someMethod(key: keyof this) {
return HelperClass.someHelp(key, this); // okay
}
}
class HelperClass {
public static someHelp<T extends MainModel>(
key: keyof T, instance: T): T[keyof T] {
return instance[key];
}
}
如果您尝试重现故障模式,您会发现这是不可能的:
class FirstModel extends MainModel {
x = 1;
}
class Oops extends MainModel { }
new Oops().someMethod("x").valueOf(); // <-- now this is a compiler error
不再需要“正确”的显式类型参数,它会自动设置为当前类的类型参数。Playground 代码链接