Extending Array in TypeScript

Typescript

Typescript Problem Overview


How to add a method to a base type, say Array? In the global module this will be recognized

interface Array {
   remove(o): Array;
}

but where to put the actual implementation?

Typescript Solutions


Solution 1 - Typescript

You can use the prototype to extend Array:

interface Array<T> {
    remove(o: T): Array<T>;
}

Array.prototype.remove = function (o) {
    // code to remove "o"
    return this;
}

If you are within a module, you will need to make it clear that you are referring to the global Array<T>, not creating a local Array<T> interface within your module:

declare global {
    interface Array<T> {
        remove(o: T): Array<T>;
    }
}

Solution 2 - Typescript

declare global seems to be the ticket as of TypeScript 2.1. Note that Array.prototype is of type any[], so if you want to have your function implementation checked for consistency, best to add a generic type parameter yourself.

declare global {
  interface Array<T> {
    remove(elem: T): Array<T>;
  }
}

if (!Array.prototype.remove) {
  Array.prototype.remove = function<T>(this: T[], elem: T): T[] {
    return this.filter(e => e !== elem);
  }
}

Solution 3 - Typescript

Adding to Rikki Gibson's answer,

export{}
declare global {
    interface Array<T> {
        remove(elem: T): Array<T>;
    }
}

if (!Array.prototype.remove) {
  Array.prototype.remove = function<T>(elem: T): T[] {
      return this.filter(e => e !== elem);
  }
}

Without the export{} TS error "Augmentations for the global scope can only be directly nested in external modules or ambient module declarations."

Solution 4 - Typescript

From TypeScript 1.6, you can "natively" extend arbitrary expressions like inbuilt types.

What's new in TypeScript: > TypeScript 1.6 adds support for classes extending arbitrary expression > that computes a constructor function. This means that built-in types > can now be extended in class declarations. > > The extends clause of a class previously required a type reference to > be specified. It now accepts an expression optionally followed by a > type argument list. The type of the expression must be a constructor > function type with at least one construct signature that has the same > number of type parameters as the number of type arguments specified in > the extends clause. The return type of the matching construct > signature(s) is the base type from which the class instance type > inherits. Effectively, this allows both real classes and "class-like" > expressions to be specified in the extends clause.

// Extend built-in types

class MyArray extends Array<number> { }
class MyError extends Error { }

// Extend computed base class

class ThingA {
    getGreeting() { return "Hello from A"; }
}

class ThingB {
    getGreeting() { return "Hello from B"; }
}

interface Greeter {
    getGreeting(): string;
}

interface GreeterConstructor {
    new (): Greeter;
}

function getGreeterBase(): GreeterConstructor {
    return Math.random() >= 0.5 ? ThingA : ThingB;
}

class Test extends getGreeterBase() {
    sayHello() {
        console.log(this.getGreeting());
    }
}

Solution 5 - Typescript

Extending Array

Here is an example to extend Array and add the remove method to it. This is a JavaScript example

/** @template T */
class List extends Array {
  /**
   * Remove an item from the list and return the removed item
   * @param {T} item
   * @return {T}
   */
  remove(item) {
    const index = this.indexOf(item);
    if (index === -1) {
      throw new Error(`${item} not in list`);
    }
    this.splice(index, 1);
    return item;
  }
}

const arr = new List(1, 2, 3);
console.log(arr.remove(3)); // 3
console.log(arr); // [1, 2]

And this is a TypeScript example.

I've added a constructor and pushed the arguments of it to the array. (Couldn't do it with super(...items)!

class List<T> extends Array {
  constructor(...items: T[]) {
    super();
    this.push(...items);
  }

  public remove(item: T): T {
    const index = this.indexOf(item);
    if (index === -1) {
      throw new Error(`${item} not in list`);
    }
    this.splice(index, 1);
    return item;
  }
}

Solution 6 - Typescript

class MyArray<T> extends Array<T> {
    remove: (elem: T) => Array<T> = function(elem: T) {
        return this.filter(e => e !== elem);
    }
}
let myArr = new MyArray<string>();
myArr.remove("some");

this works for me with typescript v2.2.1!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFrancois VanderseypenView Question on Stackoverflow
Solution 1 - TypescriptFentonView Answer on Stackoverflow
Solution 2 - TypescriptRikki GibsonView Answer on Stackoverflow
Solution 3 - TypescriptGAFView Answer on Stackoverflow
Solution 4 - TypescriptAlexView Answer on Stackoverflow
Solution 5 - TypescriptZuhair TahaView Answer on Stackoverflow
Solution 6 - TypescriptchenxuView Answer on Stackoverflow