在 Typescript 静态方法中使用 "this" 参数不会将类型缩小到当前 class?
Using "this" parameter in Typescript static methods doesn't narrow the type to the current class?
这最好用一个例子来解释。我需要在静态方法中引用当前 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'.
但是,当从另一个静态方法调用 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'.
}
}
有趣的是,如果我删除 fn2
的泛型或执行 this.fn1<typeof Cls>(true)
,它就会起作用。这意味着错误与 this
的类型有关。我认为这与 this
引用 Cls
的任何子 class 而不是恰好 Cls
有关。但是,即使 this
是 Cls
的子 class,T extends typeof Cls
仍然为真。
这是 Typescript 使用错误值 this
的错误吗?如果这不是错误,我该如何修复它?
在我的实际代码中,我需要引用当前的 class,因为该方法根据子 class.
接受不同的参数
此行为由未解析的泛型类型参数解释。
这是由基本上是高阶函数(方法)引入的间接寻址引起的。在非泛型方法 fn2
的情况下(使用 Sub
/Base
示例),当类型参数在派生 class Sub
:
Base.fn<typeof Sub>(this: typeof Sub, arg: "sub"): void
不幸的是,在fn3
的情况下,它本身就是一个泛型函数(方法),导致调用fn
时的T
泛型参数无法解析,这可以从推断的签名可以看出:
Base.fn<T>(this: T, arg: T extends typeof Sub ? "sub" : "base"): void
这澄清了以下编译器错误的含义 - 由于条件类型也未解决,因此 "sub"
和 "base"
都不能分配给 T extends typeof Sub ? "sub" : "base"
:
Argument of type '"sub"' is not assignable to parameter of type 'T extends typeof Sub ? "sub" : "base"'
正如您正确指出的那样,可以删除高阶方法的泛型类型参数,从而删除间接寻址:
static fn3(this: typeof Sub) {
this.fn('base'); // error
this.fn('sub'); // OK
}
然而,如果您需要 this
:
,这会在进一步派生的 classes 中带来复杂性
class Sub {
static fn4(this: typeof Sub) {
this.fn('sub');
return this;
}
}
class SubSub extends Sub {}
SubSub.fn4(); // typeof Sub, probably wanted typeof SubSub
不过,还有一个替代方案 - 不要限制 T
参数,而是根据 T
的推断来限制 this
的类型。 classic 技术使用的是解析为 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
内部 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
通过进一步 延迟 通过 T extends typeof Sub ? T : never
的评估,我们实际上帮助了编译器:现在它保证 T
是 typeof Sub
到实例化时。
这最好用一个例子来解释。我需要在静态方法中引用当前 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'.
但是,当从另一个静态方法调用 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'.
}
}
有趣的是,如果我删除 fn2
的泛型或执行 this.fn1<typeof Cls>(true)
,它就会起作用。这意味着错误与 this
的类型有关。我认为这与 this
引用 Cls
的任何子 class 而不是恰好 Cls
有关。但是,即使 this
是 Cls
的子 class,T extends typeof Cls
仍然为真。
这是 Typescript 使用错误值 this
的错误吗?如果这不是错误,我该如何修复它?
在我的实际代码中,我需要引用当前的 class,因为该方法根据子 class.
接受不同的参数此行为由未解析的泛型类型参数解释。
这是由基本上是高阶函数(方法)引入的间接寻址引起的。在非泛型方法 fn2
的情况下(使用 Sub
/Base
示例),当类型参数在派生 class Sub
:
Base.fn<typeof Sub>(this: typeof Sub, arg: "sub"): void
不幸的是,在fn3
的情况下,它本身就是一个泛型函数(方法),导致调用fn
时的T
泛型参数无法解析,这可以从推断的签名可以看出:
Base.fn<T>(this: T, arg: T extends typeof Sub ? "sub" : "base"): void
这澄清了以下编译器错误的含义 - 由于条件类型也未解决,因此 "sub"
和 "base"
都不能分配给 T extends typeof Sub ? "sub" : "base"
:
Argument of type '"sub"' is not assignable to parameter of type 'T extends typeof Sub ? "sub" : "base"'
正如您正确指出的那样,可以删除高阶方法的泛型类型参数,从而删除间接寻址:
static fn3(this: typeof Sub) {
this.fn('base'); // error
this.fn('sub'); // OK
}
然而,如果您需要 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
不过,还有一个替代方案 - 不要限制 T
参数,而是根据 T
的推断来限制 this
的类型。 classic 技术使用的是解析为 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
内部 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
通过进一步 延迟 通过 T extends typeof Sub ? T : never
的评估,我们实际上帮助了编译器:现在它保证 T
是 typeof Sub
到实例化时。