Can Typescript infer the type of an instance of an extension class instantiated by a method of its base?

孤街浪徒 提交于 2019-12-10 14:18:35

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!