【问题标题】:How can I extend the Array class and keep its implementations如何扩展 Array 类并保留其实现
【发布时间】:2019-06-28 14:17:28
【问题描述】:

我想向 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=&gt;x*2)) 的对象。如何在不必重写 Array 的所有方法的情况下扩展 Array 类?

【问题讨论】:

标签: typescript class-extensions


【解决方案1】:

我认为这里的问题是您的 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 syntax。当 list.map(x=&gt;x*2) 执行时,它会调用一些东西比如new List(3),你会得到一个关于3 不可迭代的错误。


那么,你能做些什么来解决这个问题?到目前为止,最简单的方法是确保 List 构造函数与 ArrayConstructor 类型兼容,方法是让它采用相同的参数类型。

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

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

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

假设您确实需要您的 List 构造函数来获取单个可迭代参数,而不是与 Array 相同的可变参数或单数参数,并假设您需要 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)) 而不是new List(x,y,z)

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

【讨论】:

  • 谢谢你,这帮助很大。
【解决方案2】:

无需设置原型。 发生错误是因为构造函数在第二次运行时 调用 map 并将数组的长度作为参数传递,因此当您尝试在 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);

 }

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

如果您将构造函数更改为使用 ... 来收集参数,则不会发生任何事情:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 2018-05-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多