[英]Using "this" parameter in Typescript static methods doesn't narrow the type to the current class?
This is best explained using an example.这最好用一个例子来解释。 I need to reference the current class in a static method, this works as expected:
我需要在 static 方法中引用当前的 class ,这可以按预期工作:
class Cls {
static fn<T extends typeof Cls>(
this: T,
arg: T extends typeof Cls ? true : false,
) {}
}
Cls.fn(true);
Cls.fn(false); // Argument of type 'false' is not assignable to parameter of type 'true'.
However, when calling Cls.fn
from another static method, it doesn't work:但是,当从另一个 static 方法调用
Cls.fn
时,它不起作用:
class Cls {
static fn1<T extends typeof Cls>(
this: T,
arg: T extends typeof Cls ? true : false,
) {}
static fn2<T extends typeof Cls>(this: T) {
this.fn1(true); // Argument of type 'true' is not assignable to parameter of type 'T extends typeof Cls ? true : false'.
}
}
Interestingly, it works if I remove fn2
's generic or do this.fn1<typeof Cls>(true)
.有趣的是,如果我删除
fn2
的泛型或执行this.fn1<typeof Cls>(true)
,它会起作用。 This means the error is related to the type of this
.这意味着错误与
this
的类型有关。 I think it has to do with this
referring to any subclass of Cls
as opposed to exactly Cls
.我认为
this
与指代Cls
的任何子类有关,而不是确切的Cls
。 However, even if this
is a subclass of Cls
, T extends typeof Cls
would still be true.但是,即使
this
是Cls
的子类, T extends typeof Cls
仍然是正确的。
Is this a bug where Typescript uses the wrong value for this
? this
是 Typescript 使用错误值的错误吗? If it's not a bug, how can I fix it?如果它不是一个错误,我该如何修复它?
In my actual code, I need to reference the current class because the method accepts different arguments depending on the subclass.在我的实际代码中,我需要引用当前的 class,因为该方法根据子类接受不同的 arguments。
Edit: here's a more realistic example using subclasses编辑: 这是一个使用子类的更现实的例子
The behavior is explained by an unresolved generic type parameter.该行为由未解析的泛型类型参数解释。
This is caused by an indirection introduced by what is basically a higher-order function (method).这是由基本上是高阶 function(方法)引入的间接引起的。 In the case of a non-generic method
fn2
(using the Sub
/ Base
example), the inference works as expected as the type parameter gets resolved to typeof Sub
in the derived class Sub
:在非泛型方法
fn2
(使用Sub
/ Base
示例)的情况下,推理按预期工作,因为 type 参数在派生的 class Sub
中解析为typeof Sub
Sub :
Base.fn<typeof Sub>(this: typeof Sub, arg: "sub"): void
Unfortunately, in the case of fn3
, it is itself a generic function (method), leading to the T
generic parameter in calls to fn
being unresolved, which can be seen from the inferred signature:不幸的是,在
fn3
的情况下,它本身就是一个泛型 function (方法),导致对fn
的调用中的T
泛型参数无法解析,这可以从推断的签名中看出:
Base.fn<T>(this: T, arg: T extends typeof Sub ? "sub" : "base"): void
This clears up what the following compiler error means - as the conditional type is also left unresolved, neither "sub"
nor "base"
will be assignable to T extends typeof Sub? "sub": "base"
这清除了以下编译器错误的含义 - 由于条件类型也未解决,
"sub"
和"base"
都不能分配给T extends typeof Sub? "sub": "base"
T extends typeof Sub? "sub": "base"
: T extends typeof Sub? "sub": "base"
:
Argument of type '"sub"' is not assignable to parameter of type 'T extends typeof Sub?
'"sub"' 类型的参数不能分配给 'T 扩展 typeof Sub 类型的参数? "sub": "base"'
“子”:“基地”'
One could, as you rightfully noted, remove the generic type parameter of the higher-order method, thus removing the indirection:正如您正确指出的那样,可以删除高阶方法的泛型类型参数,从而删除间接:
static fn3(this: typeof Sub) {
this.fn('base'); // error
this.fn('sub'); // OK
}
This, however, presents complications in further derived classes should you ever need this
:但是,如果您需要,
this
会在进一步的派生类中带来复杂性:
class Sub {
static fn4(this: typeof Sub) {
this.fn('sub');
return this;
}
}
class SubSub extends Sub {}
SubSub.fn4(); // typeof Sub, probably wanted typeof SubSub
There is an alternative, though - do not constrain the T
parameter and instead constrain the type of this
based on what T
is inferred to be.不过,还有另一种选择 - 不要限制
T
参数,而是根据推断的T
来限制this
的类型。 The classic technique is using a conditional type that resolves to itself of never
:经典技术是使用解析为
never
自身的条件类型:
class Sub extends Base {
static fn3<T>(this: T extends typeof Sub? T: never) {
this.fn('base'); // error
this.fn('sub'); // OK
return this;
}
}
Sub.fn('sub'); // OK
Sub.fn('base'); // error
Sub.fn3(); // typeof Sub
The signature of the inner this.fn()
call is inferred as:内部
this.fn()
调用的签名推断为:
Base.fn<T extends typeof Sub ? T : never>(this: T extends typeof Sub ? T : never, arg: (T extends typeof Sub ? T : never) extends typeof Sub ? "sub" : "base"): void
By further deferring the evaluation via T extends typeof Sub? T: never
通过进一步推迟评估
T extends typeof Sub? T: never
T extends typeof Sub? T: never
we actually helped the compiler: now it nows that T
is guaranteed to be typeof Sub
by the time of instantiation. T extends typeof Sub? T: never
真正帮助过编译器:现在T
在实例化时保证是typeof Sub
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.