How can I extend the Array class and keep its implementations

前端 未结 2 1771
故里飘歌
故里飘歌 2021-01-14 07:10

I\'d like to add some functions to the Array class (I\'d rather not have them as functions external to the class since it would ideally be discoverable when typing .

2条回答
  •  别那么骄傲
    2021-01-14 07:41

    I think the problem here is that your List constructor does not expect the same arguments as the Array constructor.

    When built-in methods like map() create a new array, they construct it using a constructor found in the static Symbol.species class property. This is, by default, the same as the class constructor itself... unless you override it. So List[Symbol.species] is List. And List.prototype.map() will end up calling new List(...). I'm pretty sure these methods expect the constructor at [Symbol.species] to take the same arguments as the Array constructor, namely one of these overloads:

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

    But your List constructor expects to treat its first (and only) argument items as an iterable (since it uses spread syntax on it in the call to super(...items). When list.map(x=>x*2) executes it calls something like new List(3), and you get an error about 3 not being iterable.


    So, what can you do to fix this? By far the easiest way is to make sure that your List constructor is compatible with the ArrayConstructor type, by having it take the same argument types.

    The next easiest thing to do is to override List[Symbol.species] and return the Array constructor:

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

    But that would mean that list.map(x => x*2) returns an Array and not a List.

    Assuming you really need your List constructor to take a single iterable argument instead of the same variadic-or-maybe-a-single-number arguments as Array, and assuming that you need list.map() to return a List, you can override the List[Symbol.species] property with something more complicated:

      static get [Symbol.species](): ArrayConstructor {
        return Object.assign(function (...items: any[]) {
          return new List(new Array(...items))
        }, List) as any;
      }
    

    That essentially causes native methods to call new List(new Array(x,y,z)) instead of new List(x,y,z).

    Okay, hope that makes sense and gives you some direction. Good luck!

提交回复
热议问题