打字稿:为什么 class 泛型不能分配给从“this”推断出的相同泛型?
Typescript: why is a class generic not assignable to same generic inferred from `this`?
给定如下代码,为什么Typescript在getInferred
方法中出错?是否存在 ValueOf<this>
和 T
可能不同的情况?
interface Wrapper<T> {
value: T;
}
type ValueOf<T> = T extends Wrapper<infer U> ? U : never;
class Foo<T> implements Wrapper<T> {
value: T;
constructor(value: T) {
this.value = value;
}
getInferred = (): ValueOf<this> => {
// Type 'T' is not assignable to type 'GetGeneric<this>'.
return this.value;
}
getSimple = (): T => {
// Works Fine
return this.value;
}
}
对于我的用例,我正在向 class 动态添加方法,ValueOf<this>
为动态方法提供更好的 return 类型。
const mixin = {
getFooInferred<Self extends Foo<any>>(this: Self) {
return this.getInferred();
},
getFooSimple<Self extends Foo<any>>(this: Self) {
return this.getSimple();
}
}
function makeFooWithMixin<T>(value: T) {
const foo = new Foo(value);
Object.defineProperties(foo, {
getFooInferred: {
value: mixin.getFooInferred,
},
getFooSimple: {
value: mixin.getFooSimple,
}
});
return foo as Foo<T> & typeof mixin;
}
const foo = makeFooWithMixin("hello")
// When using the returntype of `getInferred`, we correctly get `string` as the type here
const resultInferred = foo.getFooInferred()
// When using `getSimple`, we instead get `any` because `getFooSimple` types the `Self` generic as `Foo<any>`
const resultSimple = foo.getFooSimple();
polymorphic this
type is implemented as an implicit generic type parameter that all classes and interfaces have (see microsoft/TypeScript#4910)。而你的 ValueOf<T>
类型,定义为
type ValueOf<T> = T extends Wrapper<infer U> ? U : never;
是一个conditional type。所以 ValueOf<this>
是一个 条件类型 取决于 通用类型参数 .
不幸的是,TypeScript 编译器无法对可分配给此类类型的值进行太多推理。它 延迟 类型的评估,并且只有在指定 this
后才能知道它到底是什么,例如在调用 new Foo("x").getInferred()
中,其中 this
将是 Foo<string>
。在 getInferred()
的内部,this
是未指定的(它可以是 Foo<T>
的任何子类型),因此 ValueOf<this>
本质上是 opaque 给编译器。并不是 this.value
可以是 ValueOf<this>
以外的类型,而是编译器看不到它。它将拒绝任何尚未属于 ValueOf<this>
.
类型的值
如果你使用像 this.value as ValueOf<this>
这样的 type assertion,那么编译器将允许你 return,但这只是因为你声称 this.value
是类型ValueOf<this>
,而不是因为编译器可以区分一种方式:
getInferred = (): ValueOf<this> => {
return this.value as ValueOf<this>; // okay
}
一般来说,如果您需要提供通用条件类型的值,您将不得不做一些不安全的事情,比如类型断言。但在这个特定的例子中,你有一个选择。您使用 ValueOf<T>
所做的就是在 T
中查找 value
键控的 属性。这可以在没有条件类型的情况下完成。您可以使用 indexed access type 代替:
type ValueOf<T extends Wrapper<any>> = T['value']
即使编译器仍然不擅长理解泛型类型的任意操作,它确实知道如果你有一个 T
类型的值和一个 K
类型的键即使 T
或 K
是通用的,您在该键处读取的 属性 值也是 T[K]
类型。所以它应该能够验证 this.value
是类型 this["value"]
:
getInferred = (): ValueOf<this> => {
return this.value; // okay
}
确实可以。
给定如下代码,为什么Typescript在getInferred
方法中出错?是否存在 ValueOf<this>
和 T
可能不同的情况?
interface Wrapper<T> {
value: T;
}
type ValueOf<T> = T extends Wrapper<infer U> ? U : never;
class Foo<T> implements Wrapper<T> {
value: T;
constructor(value: T) {
this.value = value;
}
getInferred = (): ValueOf<this> => {
// Type 'T' is not assignable to type 'GetGeneric<this>'.
return this.value;
}
getSimple = (): T => {
// Works Fine
return this.value;
}
}
对于我的用例,我正在向 class 动态添加方法,ValueOf<this>
为动态方法提供更好的 return 类型。
const mixin = {
getFooInferred<Self extends Foo<any>>(this: Self) {
return this.getInferred();
},
getFooSimple<Self extends Foo<any>>(this: Self) {
return this.getSimple();
}
}
function makeFooWithMixin<T>(value: T) {
const foo = new Foo(value);
Object.defineProperties(foo, {
getFooInferred: {
value: mixin.getFooInferred,
},
getFooSimple: {
value: mixin.getFooSimple,
}
});
return foo as Foo<T> & typeof mixin;
}
const foo = makeFooWithMixin("hello")
// When using the returntype of `getInferred`, we correctly get `string` as the type here
const resultInferred = foo.getFooInferred()
// When using `getSimple`, we instead get `any` because `getFooSimple` types the `Self` generic as `Foo<any>`
const resultSimple = foo.getFooSimple();
polymorphic this
type is implemented as an implicit generic type parameter that all classes and interfaces have (see microsoft/TypeScript#4910)。而你的 ValueOf<T>
类型,定义为
type ValueOf<T> = T extends Wrapper<infer U> ? U : never;
是一个conditional type。所以 ValueOf<this>
是一个 条件类型 取决于 通用类型参数 .
不幸的是,TypeScript 编译器无法对可分配给此类类型的值进行太多推理。它 延迟 类型的评估,并且只有在指定 this
后才能知道它到底是什么,例如在调用 new Foo("x").getInferred()
中,其中 this
将是 Foo<string>
。在 getInferred()
的内部,this
是未指定的(它可以是 Foo<T>
的任何子类型),因此 ValueOf<this>
本质上是 opaque 给编译器。并不是 this.value
可以是 ValueOf<this>
以外的类型,而是编译器看不到它。它将拒绝任何尚未属于 ValueOf<this>
.
如果你使用像 this.value as ValueOf<this>
这样的 type assertion,那么编译器将允许你 return,但这只是因为你声称 this.value
是类型ValueOf<this>
,而不是因为编译器可以区分一种方式:
getInferred = (): ValueOf<this> => {
return this.value as ValueOf<this>; // okay
}
一般来说,如果您需要提供通用条件类型的值,您将不得不做一些不安全的事情,比如类型断言。但在这个特定的例子中,你有一个选择。您使用 ValueOf<T>
所做的就是在 T
中查找 value
键控的 属性。这可以在没有条件类型的情况下完成。您可以使用 indexed access type 代替:
type ValueOf<T extends Wrapper<any>> = T['value']
即使编译器仍然不擅长理解泛型类型的任意操作,它确实知道如果你有一个 T
类型的值和一个 K
类型的键即使 T
或 K
是通用的,您在该键处读取的 属性 值也是 T[K]
类型。所以它应该能够验证 this.value
是类型 this["value"]
:
getInferred = (): ValueOf<this> => {
return this.value; // okay
}
确实可以。