问题
Consider the following Typescript snippet:
class Animal {
constructor(name: string) {
this.name = name;
}
name: string;
haveBaby(name: string): ?? return type ?? {
return new this.constructor(name); // Error
}
}
class Cat extends Animal {}
class Dog extends Animal {}
class Gerbil extends Animal {} // etc.
let sorachi = new Cat("Sorachi"); // a: Cat
let sorachiJr = a.haveBaby("Sorachi Jr."); // I want: sorachiJr: Cat
Animals can have babies, and a baby should be the same kind of animal as the parent, i.e., should be an instance of the same class as the parent. How do I assign types in this situation, so that Typescript knows that sorachiJr: Cat?
The code snippet above doesn't work. The line return new this.constructor(name) produces the error [ts] Cannot use 'new' with an expression whose type lacks a call or construct signature. in VS Code. The only solution I was able to find and understand was replacing this.constructor(name) with (<any>this.constructor)(name) or (<any>this).constructor(name), but then the type inferred for sorachiJr is any, too, rather than Cat. I tried casting to typeof this rather than any, but got the error [ts] Cannot find name 'this'.
How can I convince Typescript that having babies is a species-preserving operation?
回答1:
The preserving the type of the class the method was called on is easy, we just use the polymorphic this type. To convince the ts that constructor will be a constructor that takes a string and returns an instance the same type as the current class requires a type assertion
type AnimalConstructor<T extends Animal> = new (name: string) => T
class Animal {
constructor(name: string) {
this.name = name;
}
name: string;
haveBaby(name: string): this {
return new (this.constructor as AnimalConstructor<this>)(name);
}
}
class Cat extends Animal {}
class Dog extends Animal {}
class Gerbil extends Animal {} // etc.
let sorachi = new Cat("Sorachi"); // a: Cat
let sorachiJr = sorachi.haveBaby("Sorachi Jr.");
Note Typescript can't validate the fact that the derived class constructor only expects a single string parameter, it might require more or less parameters. This makes this constructor not type safe.
来源:https://stackoverflow.com/questions/52841793/can-typescript-infer-the-type-of-an-instance-of-an-extension-class-instantiated