您要寻找的功能是支持static
种方法的polymorphic this
type.不幸的是,这一功能不是Typescript 的一部分.在microsoft/TypeScript#5863有一个长期开放的请求,但现在我们需要解决这个问题.
标准的解决方法似乎最早出现在this comment on ms/TS#5863中,我们使用generic类型参数和this
parameter来模拟多态this
:
abstract class AbstractInstance {
a = 1 // added some structure to avoid empty class issues
static getOwnInstance<T extends AbstractInstance>(this: new () => T) {
return new this;
}
}
在这里,您只能在类型为零参数构造函数的对象上调用getOwnInstance()
,该构造函数返回某个被约束为AbstractInstance
的类型T
.然后,因为它返回new this
,所以该方法的返回类型是T
.
因此,首先,这意味着您不能在抽象构造函数本身调用它,因为它不能直接new
able:
AbstractInstance.getOwnInstance(); // error!
//~~~~~~~~~~~~~~
// Cannot assign an abstract constructor type to a non-abstract constructor type.
并且子类将自动获得适当的返回类型:
class C1 extends AbstractInstance {
b = 2
}
const c1 = C1.getOwnInstance();
// ^? const c1: C1
c1.b.toFixed(); // okay
如果您试图重写getOwnInstance()
,您实际上将被迫以与原始实现相同的方式来执行此操作,因为调用签名和返回类型实际上不能更具体:
class C2 extends AbstractInstance { // error!
//~~
/* Class static side 'typeof C2' incorrectly extends
base class static side 'typeof AbstractInstance'.
The types returned by 'getOwnInstance()' are incompatible between these types.
Type 'C2' is not assignable to type 'T'.
'C2' is assignable to the constraint of type 'T', but 'T' could be
instantiated with a different subtype of constraint 'AbstractInstance
*/
c = 3
static getOwnInstance() {
return new C2(); // <-- oops!
}
}
这一努力失败了,因为人们不知道C2
是合适的.事实上,如果你是subclass C2
,那么你真的不想要C2
:
class C3 extends C2 {
d = 4;
}
C3.getOwnInstance() // should be C3, not C2
因此,您只能或多或少地原封不动地实现它:
class C4 extends AbstractInstance {
e = 5
static getOwnInstance<T extends AbstractInstance>(this: new () => T) {
console.log("okay I guess");
return new this;
}
}
所以这看起来不错.
这个解决方法不是perfect.如果你能将C4.getOwnInstance()
中的T
约束为C4
而不是AbstractInstance
,那就太好了,这是你可以免费获得的真正的多态this
类型. 在你不想使用的地方添加额外的泛型可能会让人感到困惑. 因此,希望有一天MS/TS#5863将得到解决,并将有一个更有吸引力的方法. 不过,就目前而言,这是我们能做的最好的事情.
Playground link to code个