将其作为抽象类方法的通用实例传递

问题描述 投票:0回答:1

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");

我不明白错误消息,是我做错了什么还是我的代码不完整?

如果我的子类型是通用的,“可以用不同的子类型实例化”是什么意思?

typescript typescript-generics
1个回答
0
投票

您在 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 类型的语言。

但在 TypeScript 中,使用 

多态

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 代码链接

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.