Typescript可以推断通过其基类方法实例化的扩展类实例的类型吗?

时间:2018-10-16 18:24:29

标签: javascript typescript constructor subclass type-inference

考虑以下Typescript代码段:

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

动物可以有婴儿,并且婴儿应与父母具有相同种类的动物,即应与父母属于同一类的实例。在这种情况下如何分配类型,以便Typescript知道sorachiJr: Cat

上面的代码段无效。在VS Code中,行return new this.constructor(name)产生错误[ts] Cannot use 'new' with an expression whose type lacks a call or construct signature.。我能够找到并理解的唯一解决方案是将this.constructor(name)替换为(<any>this.constructor)(name)(<any>this).constructor(name),但是为sorachiJr推断的类型也是any,而不是Cat。我尝试强制转换为typeof this而不是any,但收到错误[ts] Cannot find name 'this'

我如何说服Typescript相信婴儿是一种保护物种的活动?

1 个答案:

答案 0 :(得分:3)

保存调用该方法的类的类型很容易,我们只使用多态this类型。为了使ts确信constructor将是一个接受string并返回与当前类相同类型的实例的构造函数,需要类型声明

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."); 

注意:Typescript无法验证派生类构造函数仅需要单个string参数这一事实,它可能需要更多或更少的参数。这使该构造函数的类型不安全。