正确推断超类中的动态子类

时间:2018-08-16 05:55:58

标签: typescript

假设我们有一个简单的继承案例:

class Animal {
  // Return a shallow copy with all prototype functions
  getClone(): Animal {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  }
}

class Dog extends Animal {
  woof() { console.log('Woof'); }
}

因此,乍一看,此代码可以正常工作。但是,当我尝试从Dog实例使用getClone()时:

const dog = new Dog();
// Invalid since the superclass Animal doesn't contain the function woof()
dog.getClone().woof();

当然,我总是可以通过以面向对象的方式重写getClone()来解决此错误:

class Dog extends Animal {
  woof() { console.log('Woof'); }
  getClone(): Dog {
    return super.getClone();
  }
}

但是,可以说该项目扩大了规模,我需要创建100个动物类。在每个类中编写重写函数会很麻烦吗?另外,如果我需要在动物基类中添加一个函数,是否需要在每个子类中重载它?

因此,我想出了一个临时解决方案:使用泛型。

class Animal<T> {
  getClone(): T {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  }
}

class Dog extends Animal<Dog> {
  woof() { console.log('Woof'); }
}

那么这段代码可以很好地工作,没有TSLint或编译错误。但是,如果在类创建后立即将泛型添加到超类中,真的安全吗?

它也有预期的缺陷。假设我们有一个名为changeNameThenGet(name: string)的函数。如果未提供名称或名称无效,它将返回自身:

class Animal<T> {
  name = 'Some Animal';
  getClone(): T {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  }
  changeNameThenGet(name: string): T {
    if (!name) {
      // Typescript error. 'this' is an instance of Animal, not T
      return this;
    }
    const newAnimal = this.getClone();
    newAnimal.name = name;
    return newAnimal;
  }
}

似乎Typescript无法识别T是Animal的子类,我正在尝试寻找一种使其正常工作的方法。请注意,class Animal<T extends Animal<any>>也不起作用。我可以说return this as any可以解决这个问题,但归根结底,这只是一种解决方法。

那么你们认为解决此类问题的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

通常使用通用方法,因为在大多数语言中,没有办法告诉基类中的方法返回什么当前类型。幸运的是Typescript不是大多数语言。 Typescript允许在非静态方法中将this用作类型,这正是您所需要的:

class Animal {
    // Return a shallow copy with all prototype functions
    getClone(): this {
        return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
    }
}

class Dog extends Animal {
    woof() { console.log('Woof'); }
}

new Dog().getClone().woof();

这是介绍此功能的Issue