给出以下代码,为什么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;
}
}
对于我的用例,我将动态地向类添加方法,ValueOf<this>
为动态方法提供了更好的返回类型。
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();
上面所有代码的Typescript playground链接
多态this
类型被实现为所有类和接口都具有的隐式泛型类型参数(参见microsoft/TypeScript#4910)。你的ValueOf<T>
类型,定义为
type ValueOf<T> = T extends Wrapper<infer U> ? U : never;
是条件类型。所以ValueOf<this>
是一个条件类型,它依赖于泛型参数。
不幸的是,TypeScript编译器不能做太多的推理,哪些值可以赋值给这样的类型。它延迟对类型的求值,并且只有在指定了this
之后才能知道它的真实值,例如在调用new Foo("x").getInferred()
中,其中this
将是Foo<string>
。在getInferred()
的主体中,this
是未指定的(它可以是Foo<T>
的任何子类型),因此ValueOf<this>
对编译器来说本质上是不透明的。这并不是说this.value
可以是ValueOf<this>
以外的类型,而是编译器看不到它。它将拒绝任何不是ValueOf<this>
类型的值。
如果你使用像this.value as ValueOf<this>
这样的类型断言,那么编译器将允许你返回它,但这只是因为你声称this.value
是ValueOf<this>
类型,而不是因为编译器可以告诉你一种方式或另一种方式:
getInferred = (): ValueOf<this> => {
return this.value as ValueOf<this>; // okay
}
一般来说,如果需要提供泛型条件类型的值,就必须做一些不安全的事情,比如类型断言。但在这种特殊情况下,你有另一种选择。您使用ValueOf<T>
所做的一切都是在T
中查找value
- keyyed属性。没有条件类型也可以做到这一点。您可以使用索引访问类型:
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
}
确实可以。
Playground链接到代码