在TypeScript中为类继承返回新的self等价



我有一个基类Collection,它提供了基本的数组功能。这个类可以扩展到其他特定于领域的用例。当一个"破坏"方法(如filter)被调用时,它应该返回一个包含过滤元素的新实例(以继续使用类方法,而不仅仅是返回数组)。

在PHP中,您将使用return new self()来返回实际的子或父类,基于它的构造(我认为对于Java来说,它是return obj.newInstance())。但与JS/TS我真的很难找到一个解决方案。我目前的解决方法是覆盖所有子类的newInstance方法。

有解决办法吗?

class Collection<E> {
protected items: E[];
constructor(items: any[] = []) {
this.items = items;
}
// doesn't work, just one of many attempts
protected newInstance(items: E[]) {
return new Collection(items);
//return new this.constructor(items); // "This expression is not constructable"
// return new this.constructor.prototype(items); // another attempt, says "this.constructor.prototype is not a constructor"
}
size() {
return this.items.length;
}
// should filter and return new instance to use class methods
filter(callback: (item: any, index?: number) => boolean): this {
// should be the actual instance (can be inherited)
return this.newInstance(this.items.filter(callback)) as this;
}
}
class NumberCollection extends Collection<number> {
sum() {
return this.items.reduce((a, b) => a + b, 0);
}
}
let numbers = new NumberCollection([1, 2, 3, 4]);
console.log(numbers.sum()); // works,
// throws "sum() is not a function"
console.log(numbers.filter((n) => n > 1).sum());

如果要使用类型安全,请使用:

const { constructor } = Object.getPrototypeOf(this);
new constructor(...args);

参见参考资料同一类

这个答案似乎不正确(我很高兴地说),看看这个答案。但是,我将保留这个答案的其余部分,直到/除非我可以删除它。


遗憾的是,这在JavaScript中很容易,但在TypeScript中却令人难以置信地尴尬。

在JavaScript中,你会做两件事之一:

  • new this.constructor(/*....*/)如你所述。

  • 物种格局。

不幸的是,如果在TypeScript中没有类型断言或直接的@ts-ignore,你就无法做到这一点。看看这个关于物种模式的相关问题,答案是:你不能那样做。

我认为你唯一现实的选择,如果filter(和这样的)总是返回一个类的实例,他们被调用(所以它是上面的#1,而不是#2),是做你所做的与this作为返回类型注释,使用new this.constructor(/*...*/)@ts-ignore在它:

protected newInstance(items: E[]): this {
// @ts-ignore - blech
return new this.constructor(items);
}

操场上联系

正确地创建了它所调用的类的实例(即使TypeScript没有看到this.constructor的构造签名),因为默认情况下,分配给类实例的原型有一个constructor属性,该属性指向构造函数:通过new Collection创建的实例中的this.constructorCollection;通过new NumberCollection创建的实例中的this.constructorNumberCollection。(你可以做一些事情来把它搞砸,但是class语法可以很容易地避免其中的大多数。)因此,new this.constructor(/*...*/)使用(在正常情况下)用于创建this的构造函数创建一个新对象。因此,你不再有sum的问题;如此:

let numbers = new NumberCollection([1, 2, 3, 4]);
console.log(numbers.sum());
const x = numbers.filter((n) => n > 1);
//    ^? −− type is NumberCollection
console.log(x.sum()); // works

我不喜欢它,但据我所知,我们现在只能这样了。

最新更新