Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an extension method for a specific type of generic type in TypeScript

For a semi-educational side-project I'm working on, I've got a TypeScript interface that exposes an id variable on any object that implements it. In this app, it's reasonably common for there to be Array<>s of these IIdable objects.

Something I'm trying to do, since this application does not use a database, is implement an auto-incrementor to the service that keeps track of these list of IIdables. Naturally, I considered adding an extension the "JavaScript" way as below...

Array<MyProject.Models.IIdable>.prototype.getNextId = () => {
    let biggestId: number = 0;
    for (let current of this) {
        if (current.id > biggestId)
            biggestId = current.id;
    }

    return biggestId + 1;
};

...But Visual Studio Code throws a couple of errors, one of which is:

Cannot find name 'prototype'

I find this questionable, given that Array<T>s are a JavaScript 'primitive', and given that everything is an object in JavaScript (thus some of the problems that TypeScript was created to diminish or avoid outright.) However, I realize I know just enough of both JavaScript and TypeScript to be "dangerous", meaning I know enough things to permit myself to do something quite dumb.

If I were using C# - which TypeScript is not - I would write such an extension method something like this:

public static GetNextId(this List<IIdable> source)
{
    // ...Read each IIdable's ID to figure out the next biggest to give.
}

...Unfortunately, I can't quite do that to my knowledge. I did some research on writing extension methods in TypeScript prior to posting this question. One such answer was to modify the host type's interface with the new method, which in this case I don't want to do. I only want to add an extension for cases when we're dealing with an Array<IIdable>, and no others.

Thus, I must ask: In what way can I write an extension method on a generic type, for a specific type of thing, in TypeScript?

like image 558
Andrew Gray Avatar asked Oct 16 '18 21:10

Andrew Gray


People also ask

How do you pass a generic type as parameter TypeScript?

Assigning Generic Parameters By passing in the type with the <number> code, you are explicitly letting TypeScript know that you want the generic type parameter T of the identity function to be of type number . This will enforce the number type as the argument and the return value.

What is generic extension?

Generics are a way to tailor a class or method to a specific type. A generic method or class is designed to work for any type. This is most easily illustrated in the List<T> class, where it can be tailored to be a list of any type. This gives you the type-safety of knowing the list only contains that specific type.


1 Answers

The generic class Array only has one prototype; you can't specify a type argument when accessing the prototype. However, you can override the this type on the method. Here's how you would declare and then define the method. Note that you have to use a function function, which is capable of receiving a this parameter, and not an arrow function, which always uses the this that was in scope where it was defined (which in this case is the global object).

interface Array<T> { 
  getNextId(this: MyProject.Models.IIdable[]): number;
}

// The `this` type for the function is taken from the declaration above.
Array.prototype.getNextId = function() {
    let biggestId: number = 0;
    for (let current of this) {
        if (current.id > biggestId)
            biggestId = current.id;
    }

    return biggestId + 1;
};
like image 69
Matt McCutchen Avatar answered Nov 14 '22 03:11

Matt McCutchen