Search code examples
javascriptextend

Extend base Array class in JavaScript


I have a custom array class that extends the base array class. I have a custom method for ease of use

export class ExampleArray extends Array {
    includesThing(thing) {
        ...

        return false
    }
}

However the existing methods of filter, map etc return an instance of an array. I would like to return an instance of ExampleArray with these methods.

I can find the interface for these methods, but not their implementation. How do I call the parent method and return my custom EampleArray instead? Something like the following

export class ExampleArray extends Array {
    filter() {

    result = Array.filter()
    array = new ExampleArray()
    array.push(...result)

    return array
}

Or is this even the correct way to extend an Array to make a custom array?


Solution

  • You will need to shadow the existing .filter and .map so that, when called on an instance of ExampleArray, your new functions will be called, rather than the Array.prototype functions. Inside ExampleArray, you can access super.map and super.filter in order to get to the Array.prototype methods. For example:

    class ExampleArray extends Array {
      constructor(...args) {
        super(...args);
      }
      hasMoreThanTwoItems() {
        // example custom method
        return this.length > 2;
      }
      isExampleArray() {
        return true;
      }
      
      // Shadow Array.prototype methods:
      filter(...args) {
        return new ExampleArray(
          // Spread the result of the native .filter into a new ExampleArray instance:
          ...super.filter.apply(this, args)
        );
      }
      map(...args) {
        return new ExampleArray(
          ...super.map.apply(this, args)
        );
      }
    }
    
    const exampleArray = new ExampleArray(3, 4, 5, 6, 7);
    
    // true, filtering will result in 3 items
    console.log(
      exampleArray
        .filter(e => e > 4)
        .hasMoreThanTwoItems()
    );
    
    // false, filtering will result in zero items
    console.log(
      exampleArray
        .filter(e => e > 10)
        .hasMoreThanTwoItems()
    );
    
    // true, is an ExampleArray
    console.log(
      exampleArray
        .map(e => e * 2)
        .isExampleArray()
    );

    Note that there are also other Array methods which return arrays, including splice, slice, and (experimental) flat and flatMap. If you want those to return a custom class instantiation rather than the default Array instance, follow the same pattern: shadow the Array.prototype function name, and return a new ExampleArray populated with the result of applying the Array.prototype method:

    <fnName>(...args) {
      return new ExampleArray(
        ...super.<fnName>.apply(this, args)
      );
    }