5

I want to implement an array-like class that:

  1. accepts array as constructor's parameter
  2. should be iterable and have all built-in array's methods
  3. have some custom methods
  4. Should be able to extend other class

I see it like this:

class BaseModel {
  arr: Array;

  constructor(arr: Array<any>) { // <= req. #1
    this.arr = arr;
  }

  serialize(arr) { // <= req. #3
    this.arr = arr;
  }
}

class ListModel extends BaseModel { // <= req. #4
  constructor(arr: Array<any>) { // <= req. #1
    super(arr);
  }

  sayHello() { // <= req. #3
    console.log('hello');
  }
}

let list = new ListModel([1,2,3]);
list.sayHello();
// expected output:
// 'hello'
list.push(4); // <= req. #2

for (let a of list) { // <= req. #2
  console.log(a);
}
// expected output:
// 1
// 2
// 3
// 4
list.serialize([2,3]);

for (let a of list) {
  console.log(a);
}
// expected output:
// 2
// 3

Is it possible with typescript? I looked for solution but haven't found something closer to these requirements. Thx!

1
  • what is the problem? Is something not working? Commented May 11, 2016 at 12:57

2 Answers 2

10

You can extend the Array class and by doing that establishing requirement #2.
Here's an implementation that I think matches all of your requirements:

class MyArray<T> extends Array<T> {
    constructor(items?: T[]) {
        super();
        items && this.addItems(items);
    }

    public serialize(items: T[]): void {
        this.splice(0, this.length);
        this.addItems(items);
    }

    private addItems(items: T[]) {
        items.forEach(item => this.push(item));
    }
}

class StringsList extends MyArray<string> {
    public sayHello(): void { // req. #3
        console.log("hello");
    }
}

class NumbersList extends MyArray<number> {
    public sum(): number { // req. #3
        return this.reduce((prev: number, current: number) => prev + current);
    }
}

If you don't like the generics part, you can simply extend Array<any>.

Examples:

let arr1 = new MyArray(["hey", 4, true]);
console.log(arr1); // ["hey", 4, true]

arr1.push(99);
console.log(arr1); // ["hey", 4, true, 99]

arr1.forEach(item => console.log(item)); // hey, 4, true, 99

let arr2 = new StringsList(["str1", "str2"]);
console.log(arr2); // ["str1", "str2"]

arr2.serialize(["str3", "str4"]);
console.log(arr2); // ["str3", "str4"]

let arr3 = new NumbersList([1,2,3,4,5]);
console.log(arr3.sum()); // 15
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the reply, I also came to this approach yesterday. It seems working for me
3

It seems that extending Arrays is a no-no in Typescript, I was getting runtime errors and I found this excellent article to do it in the proper way: https://blog.simontest.net/extend-array-with-typescript-965cc1134b3

Our custom class instances needs to be created like a Singleton to assign the proper prototype, and do not inherit the Array prototype on runtime:

export class MyArray<T> extends Array<any> {

  static create<T>(): MyArray<T> {
    return Object.create(MyArray.prototype);
  }

  ...
}

Without this, the custom methods do not exists for JavaScript in runtime.

1 Comment

This seems either outdated or wrong, since when I tried the code given in the article you linked, it worked without a hitch, including compiled JavaScript jsfiddle.net/ub4Locdr

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.