提问人:Apochotolasys 提问时间:4/6/2023 最后编辑:Apochotolasys 更新时间:4/6/2023 访问量:67
在打字稿中,“this”类型如何真正推断其类型?
In typescript, how does the 'this' type really infer its type?
问:
在打字稿中,我想使用 keywork 来键入类的一些属性。但是,我面临着一个我无法解决的问题。
我想做的是这样的: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"]>'
}
}
这样做,我有错误:在第 10 行。
但是,此代码运行良好:Type 'Animal' is not assignable to type 'InstanceType<this["AnimalConstructor"]>'
export class Animal{
/*some properties*/
}
export class Population{
AnimalConstructor : typeof Animal = Animal;
animal : Animal;
createAnimal(){
this.animal = new this.AnimalConstructor();
}
}
我不明白为什么最后一个有效,而第一个无效。这可能是由于编译器推断关键字类型的方式所致。但我也没有找到任何能够解释这种行为的文档。官方文档只说:“一个名为 this 的特殊类型动态地引用了当前类的类型”——这并不能解释为什么上面的例子不起作用。this
答:
多态 this
类型充当隐式泛型类型参数,该参数约束为当前类类型,但仅在访问类或其子类的特定实例时指定为类型参数。(请参阅 microsoft/TypeScript#4910,了解描述为隐式类型参数的实现拉取请求。这意味着,当您使用该类型时,您将获得泛型的优点和缺点。this
this
InstanceType<T>
实用程序类型是作为条件类型实现的,从其定义中可以看出:
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
因此,在类定义的主体中,类型是泛型条件类型(依赖于至少一个尚未指定的类型参数的条件类型)。而且,不幸的是,编译器无法真正推理这些类型。Population
InstanceType<this['AnimalConstructor']>
在计算值时,编译器会将表观类型 to 加宽,因为您正在访问泛型类型化值的特定属性,并且编译器将此扩展作为近似值进行,以使事情变得更容易。(有关来源,请参阅 microsoft/TypeScript#33181 上的此评论。所以被看作是类型,因此被看作是类型:new this.AnimalConstructor()
this
Animal
this.AnimalConstructor
typeof Animal
new this.AnimalConstructor()
Animal
const ac = this.AnimalConstructor;
//const ac: typeof Animal
const a = new ac();
//const a: Animal;
但是编译器推迟了对泛型条件类型(如 )的计算,因此最终将此类类型视为本质上是不透明的。如果尝试为此类类型的变量赋值,编译器几乎肯定会抱怨,因为它无法验证该值是否与该类型兼容。作为一个人,你可以检查值是并理解条件类型背后的含义,并说“是的,这很好”,但编译器大多将类型视为一个黑盒,它不知道什么可能与它兼容。(最接近此问题的文档是 microsoft/TypeScript#33912)。InstanceType<this["AnimalConstructor"]>
所以你得到一个错误:
this.animal = a; // error!
// Type 'Animal' is not assignable to
// type 'InstanceType<this["AnimalConstructor"]>' 😟
如果你想保持你的类型不变,那么最好的方法可能是接受你比编译器更聪明。既然你确信无论子类中出现什么,它都明显属于类型,那么你可以向编译器断言这个事实,以阻止它担心它无法弄清楚的事情:new this.AnimalConstructor()
InstanceType<this["AnimalConstructor"]>
this
createAnimal() {
const ac = this.AnimalConstructor;
const a = new ac();
this.animal = a as InstanceType<this['AnimalConstructor']>; // okay
}
或者只是
createAnimal() {
this.animal = new this.AnimalConstructor() as typeof this.animal; // okay
}
这行得通,你可以继续前进。如果编译器更聪明,它不像类型安全那样安全,但它可能是您在不重构的情况下可以做到的最好的......例如,要在实例类型中显式泛型,以便您可以控制何时扩大泛型并完全避免条件类型:Population
AnimalConstructor
export class Population<T extends Animal> {
constructor(public AnimalConstructor: new () => T) {
this.animal = new AnimalConstructor(); // things should be initialized
}
animal: T
createAnimal() {
this.animal = new this.AnimalConstructor(); // okay
}
}
评论
this
this
Population
DogPopulation
animal
Dog
createAnimal()
this
Population
animal
DogPopulation
Animal
this.animal = new Animal();
this.animal = new this.AnimalConstructor();
this
InstanceType
InstanceType<this["xxx"]>
this.animal = new (this.AnimalConstructor.bind(this.AnimalConstructor))();
this.animal
Animal