我有一个基类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.constructor
是Collection
;通过new NumberCollection
创建的实例中的this.constructor
是NumberCollection
。(你可以做一些事情来把它搞砸,但是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
我不喜欢它,但据我所知,我们现在只能这样了。