6

I'm having some troubles with a custom class extending Array. I basically just want to add a few helper methods on the Array class, here I'm adding a logMe method, with a prefix to the array.

class AAA extends Array {
  _prefix: string;

  constructor(prefix: string, values: number[]) {
    console.log('AAA contructor', values);

    // Use hack
    // https://stackoverflow.com/questions/35673043/extending-array-from-typescript
    super();
    this.push(...values);

    this._prefix = prefix;
  }

  logMe() {
    console.log('The prefix is:', this._prefix);
    console.log('The values are:');
    this.map(x => x * 2).forEach(console.log);
  }
}

And here's my test:

const a = new AAA('PREFIX-A', [1, 2, 3]);
a.logMe();

Expected result:

AAA contructor [ 1, 2, 3 ]
The prefix is: PREFIX-A
The values are: 1, 2, 3

Actual result:

AAA contructor [ 1, 2, 3 ]
The prefix is: PREFIX-A
AAA contructor undefined

/Users/amaurymartiny/Workspaces/test-array/a.ts:7
    this.push(...values);
         ^
TypeError: Cannot read property 'Symbol(Symbol.iterator)' of this.push
    at new AAA (/Users/amaurymartiny/Workspaces/test-array/a.ts:7:10)
    at AAA.map (<anonymous>)
    at AAA.logMe (/Users/amaurymartiny/Workspaces/test-array/a.ts:13:41)
    at Object.<anonymous> (/Users/amaurymartiny/Workspaces/test-array/a.ts:18:3)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Module.m._compile (/Users/amaurymartiny/Workspaces/test-array/node_modules/ts-node/src/index.ts:439:23)
    at Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Object.require.extensions.(anonymous function) [as .ts] (/Users/amaurymartiny/Workspaces/test-array/node_modules/ts-node/src/index.ts:442:12)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)

This is quite strange to me, why is the constructor called again when I call this.map?

TypeScript 3.1.3. NodeJS 10.12.0. I already implemented this related thread.

6
  • Are you intending to call the native map function on Array? Doesn't that return a new array? Commented Oct 23, 2018 at 13:36
  • You're logging the call the console.log didn't you mean to write console.log('The values are:', this.join(", ")) Commented Oct 23, 2018 at 13:41
  • @FrankModica Yes, I'm intending to call the native map. Yes, that returns a new Array, so should call the constructor of Array, not the one of AAA. Commented Oct 23, 2018 at 13:43
  • @Motti Fixed. I actually want the child class to use the map method, Commented Oct 23, 2018 at 13:45
  • Oh you're right, interesting! Commented Oct 23, 2018 at 13:49

1 Answer 1

2

Try this. It worked for me.

class Collection extends Array<xxx> {    
   constructor(documents: Array<xxx> | number) {
      if(documents instanceof Array) super(...documents);
      else super(documents);
      Object.setPrototypeOf(this, Object.create(Collection.prototype));
   }      
   public get aMethodUsingMapAsExample(): string[] {
      // this.map invokes the constructor with the length passed in as argument
      var types = this.map(e => e.documentType);
      types = types.sort();
      return types;
   }  
}

explanation: native array constructor has 2 overloads. You have to support both of them in your extending class. This is because this.map, behind the scenes, creates a new array, thereby invoking the constructor with the length of the array.

  • ArrayConstructor(...items: any[]): Here you must call the spread operator for the array to be constructed correctly
  • ArrayConstructor(arrayLength: number). Calling the spread operator on object of type number will throw an exception.
Sign up to request clarification or add additional context in comments.

2 Comments

there is an exception if you pass an array that contains one single element of type number it will consider the single element as a the length of the array solution if (arr instanceof Array) { if (arr.length == 1) { super(); this.push(arr[0]); } else { super(...arr); } }
In the fringe of this topic, with ts transpilation, new Collection() gave me an Array instead of Collection +1 for the Object.setPrototypeOf(this, Object.create(Collection.prototype)); trick ;)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.