在typescript中,我想使用this
键来键入我的类的一些属性。然而,我面临着一个我无法解决的问题。我想做的是:
export class Animal{
/*some properties*/
}
export class Population{
AnimalConstructor : typeof Animal = Animal;
animal : InstanceType<this['AnimalConstructor']>;
createAnimal(){
this.animal = new this.AnimalConstructor();//Type 'Animal' is not assignable to type 'InstanceType<this["AnimalConstructor"]>'
}
}
这样做,我有错误:Type 'Animal' is not assignable to type 'InstanceType<this["AnimalConstructor"]>'
在第10行。然而,这段代码运行良好:
export class Animal{
/*some properties*/
}
export class Population{
AnimalConstructor : typeof Animal = Animal;
animal : Animal;
createAnimal(){
this.animal = new this.AnimalConstructor();
}
}
我不明白为什么最后一个可以工作,而第一个不行。这可能是由于编译器推断this
关键字类型的方式。但我也没有找到任何文档能够解释这种行为。官方文档只说:'一个叫做this的特殊类型动态引用当前类的类型。' -这并不能解释为什么上面的例子不起作用。
1条答案
按热度按时间uyto3xhc1#
多态
this
类型充当一个隐式generic类型参数,它被约束到当前类类型。但只有在访问类或其子类的特定示例时才指定为类型参数。(有关将this
描述为隐式类型参数的实现拉取请求,请参见microsoft/TypeScript#4910。)这意味着当您使用this
类型时,您可以获得泛型的优点和缺点。InstanceType<T>
实用程序类型被实现为conditional type,正如您从其定义中所看到的:因此,在
Population
类定义的主体中,类型InstanceType<this['AnimalConstructor']>
是一个 * 泛型条件类型 *(一个依赖于至少一个尚未指定的类型参数的条件类型)。不幸的是,编译器无法真正推理出这样的类型。当计算值
new this.AnimalConstructor()
时,编译器将this
的表观类型扩展为Animal
,因为您正在访问泛型类型值的特定属性,编译器将这种扩展作为一种近似,使事情变得更容易。(参见microsoft/TypeScript#33181上的注解以获取源代码。)因此this.AnimalConstructor
被视为类型typeof Animal
,因此new this.AnimalConstructor()
被视为类型Animal
:但是编译器 * 推迟 * 对
InstanceType<this["AnimalConstructor"]>
等通用条件类型的求值,因此最终将此类类型视为本质上 * 不透明 * 的类型。如果您尝试为此类类型的变量赋值,编译器几乎肯定会抱怨,因为它无法验证值是否与类型兼容。作为一个人,你可以检查值is并理解条件类型背后的含义,然后说“是的,这很好”,但是编译器通常将类型视为一个黑盒,并且不知道什么可能与它兼容。(与此问题最接近的文档是microsoft/TypeScript#33912)。你得到一个错误:
如果你想保持你的类型不变,那么最好的方法可能就是接受你比编译器更聪明的事实。既然你确定
new this.AnimalConstructor()
的类型是InstanceType<this["AnimalConstructor"]>
,不管this
在子类中是什么,那么你可以直接向编译器Assert这个事实,以避免它担心它无法解决的问题:或者只是
如果编译器更聪明的话,它可能不像类型安全的那样,但这可能是你在不重构的情况下所能做的最好的事情......例如,在
AnimalConstructor
的示例类型中使Population
* 显式 * 泛型,这样你就可以控制泛型何时扩大,并完全避免条件类型:Playground链接到代码