我想向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类?
答案 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 syntax。 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)
返回一个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>) { //...