我如何扩展Array类并保留其实现

时间:2019-02-04 19:21:52

标签: typescript class-extensions

我想向Array类添加一些函数(我不希望将它们作为类外部的函数使用,因为理想情况下,在对象后键入.时可以发现它们)。这是我到目前为止的内容:

export class List<T> extends Array<T> {
    constructor(items?: Array<T>) {
        super(...items)
        Object.setPrototypeOf(this, List.prototype);
    }

    get first(): T {
        return this[0]
    }
}

运行正常:

const list = new List([1,2,3]);
console.log(list.first)

但是如果我尝试运行此命令:

const list = new List([1,2,3]);
console.log(list.map(x=>x*2))

我收到以下错误:

        super(...items)
        ^
TypeError: Found non-callable @@iterator

理想情况下,我会得到一个相当于new List(this.map(x=>x*2))的对象。如何在不重写Array的所有方法的情况下扩展Array类?

2 个答案:

答案 0 :(得分:2)

我认为这里的问题是您的List构造函数期望的参数与Array构造函数不同。

map()之类的内置方法创建一个新数组时,它们使用static Symbol.species class property中的构造函数来构造它。默认情况下,这与类构造函数本身相同...除非您重写它。因此List[Symbol.species]List。并且List.prototype.map()将最终调用new List(...)。我很确定这些方法希望[Symbol.species]的构造函数采用the same arguments as the Array constructor,即这些重载之一:

new Array(element0, element1[, ...[, elementN]]); // variadic arguments, one per item in array
new Array(arrayLength); // one numeric argument specifying length 

但是您的List构造函数希望将其第一个(也是唯一一个)参数items视为可迭代的(因为它在对super(...items)的调用中使用了spread syntaxlist.map(x=>x*2)执行时,它会调用类似new List(3)的内容,并且您会收到有关3不可迭代的错误。


那么,您该怎么做才能解决此问题?到目前为止,最简单的方法是通过使用相同的参数类型来确保List构造函数与ArrayConstructor类型兼容。

接下来要做的最简单的事情是覆盖List[Symbol.species]并返回Array构造函数:

  static get [Symbol.species](): ArrayConstructor {
    return Array;
  }

但这意味着list.map(x => x*2)返回一个Array而不是一个List

假设您确实需要List构造函数接受一个可迭代的参数,而不是与Array相同的可变或单数参数,并假设您需要{{ 1}}返回list.map(),您可以用更复杂的方法覆盖List属性:

List[Symbol.species]

从本质上讲,这将导致本机方法调用 static get [Symbol.species](): ArrayConstructor { return Object.assign(function (...items: any[]) { return new List(new Array(...items)) }, List) as any; } 而不是new List(new Array(x,y,z))

好的,希望这有意义并能给您一些指导。祝你好运!

答案 1 :(得分:1)

无需设置原型。 发生错误是因为构造函数在以下情况下第二次运行 会调用map并将数组的长度作为参数传递,因此当您尝试在超级调用中扩展参数时,由于数字不可迭代,因此会引发错误。

 constructor(items?: Array<T>) {

    console.log(`I've received `, items);
    items = items || [];
    super(...items);
    console.log(`Now i'm this`, this); //
    // Object.setPrototypeOf(this, List.prototype);

 }

为什么会发生?不知道! 我还没有足够的分数,否则我将其作为评论! :-)

如果将构造函数更改为使用...来收集参数,则不会爆炸:

 constructor(...items: Array<T>) { //...