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 ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.