Typescript:为什么一个泛型类不能赋值给从' 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;
}
}

对于我的用例,我将动态地向类添加方法,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.valueValueOf<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]类型的,即使TK是泛型的。因此,它应该能够验证this.value的类型为this["value"]:

getInferred = (): ValueOf<this> => {
return this.value; // okay
}

确实可以。

Playground链接到代码

相关内容

最新更新