3

我想向 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 的所有方法的对象?

4

2 回答 2

11

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

当像创建一个新数组这样的内置方法时,它们使用静态类属性map()中的构造函数来构造它。默认情况下,这与类构造函数本身相同......除非您覆盖它。也是如此。并且最终会调用. 我很确定这些方法期望构造函数 at采用与构造函数相同的参数,即以下重载之一:Symbol.speciesList[Symbol.species]ListList.prototype.map()new List(...)[Symbol.species]Array

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

但是您的构造List函数希望将其第一个(也是唯一的)参数视为items可迭代的(因为它在调用.super(...items)list.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)返回 anArray而不是 a List

假设您确实需要您的List构造函数采用单个可迭代参数而不是与 相同的可变参数或可能是单个数字参数Array,并假设您需要list.map()返回 a ,您可以用更复杂的东西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))而不是new List(x,y,z).

好的,希望这是有道理的,并给你一些方向。祝你好运!

于 2019-02-05T01:21:36.610 回答
1

There is no need to set the prototype. The error occurs because the constructor runs a second time when the map is called and the length of the array is passed as an argument, so when you try to spread the argument on the super call, it throws an error because a number is not iterable.

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

 }

Why does it happen? No idea! I do not have enough points yet, otherwise I'd put this as a comment! :-)

If you change the constructor to use ... to gather the arguments nothing will blow up:

 constructor(...items: Array<T>) { //...
于 2019-02-05T00:58:42.270 回答