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

Mik*_*ike 1 class-extensions typescript

我想向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]
    }
}
Run Code Online (Sandbox Code Playgroud)

运行良好:

const list = new List([1,2,3]);
console.log(list.first)
Run Code Online (Sandbox Code Playgroud)

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

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

我收到以下错误:

        super(...items)
        ^
TypeError: Found non-callable @@iterator
Run Code Online (Sandbox Code Playgroud)

理想情况下,我将获得一个对象,该对象等效于new List(this.map(x=>x*2))如何在无需重写Array的所有方法的情况下扩展Array类?

jca*_*alz 6

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

当诸如map()创建新数组之类的内置方法时,它们使用在static Symbol.speciesclass属性中找到的构造函数来构造它。默认情况下,这与类构造函数本身相同...除非您重写它。所以List[Symbol.species]List。并且List.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 
Run Code Online (Sandbox Code Playgroud)

但是您的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;
  }
Run Code Online (Sandbox Code Playgroud)

但这意味着list.map(x => x*2)返回Array而不是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;
  }
Run Code Online (Sandbox Code Playgroud)

从本质上讲,这会导致调用本机方法new List(new Array(x,y,z))而不是new List(x,y,z)

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


小智 1

无需设置原型。发生错误的原因是构造函数在调用映射时第二次运行,并且数组的长度作为参数传递,因此当您尝试在 super 调用上传播参数时,它会抛出错误,因为数字不可迭代。

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

 }
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况?不知道!我还没有足够的积分,否则我会把它作为评论!:-)

如果您将构造函数更改为使用 ... 来收集参数,则不会出现任何问题:

 constructor(...items: Array<T>) { //...
Run Code Online (Sandbox Code Playgroud)