Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: custom class extending Array

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.

like image 800
jeanpaul62 Avatar asked Oct 23 '18 13:10

jeanpaul62


People also ask

Can a class extend an array?

Built-in classes like Array, Map and others are extendable also.

Can JavaScript arrays be Extended?

To extend an existing array in JavaScript, use the Array. concat() method.

How do I combine two arrays in TypeScript?

TypeScript - Array concat() concat() method returns a new array comprised of this array joined with two or more arrays.


1 Answers

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.
like image 130
hannes neukermans Avatar answered Oct 10 '22 03:10

hannes neukermans