Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a static function on a generic class in TypeScript

Tags:

Given a generic class Foo<T> with a static factory method:

class Foo<T>
{
    public static factory(item: T): Foo<T>
    {
        return null;
    }
}

Why does this not compile?

var f = Foo<number>.factory(1);

The error message:

error TS2069: A parameter list must follow a generic type argument list. '(' expected.

This does compile however:

var f = Foo<number>().factory(1);

Why are the parenthesis required? Is this invoking the constructor?

like image 890
Drew Noakes Avatar asked Jun 18 '14 16:06

Drew Noakes


2 Answers

Just as static methods cannot access instance members, the static method cannot use the instance type argument.

For this reason, your static method must be generic and accept a type argument. I highlight this by using U in the static function, and T in the class. It is important to remember that the instance type of T is not the same as the static method type of U.

class Foo<T>
{
    public static factory<U>(item: U): Foo<U>
    {
        return new Foo<U>();
    }

    instanceMethod(input: T) : T
    {
        return input;
    }
}

You then call it by passing the type argument just before the parenthesis, like this:

var f: Foo<number> = Foo.factory<number>(1);

When type inference is possible, the type annotation may be dropped:

var f: Foo<number> = Foo.factory(1);

The variable f is an instance of Foo with a type argument of number, so the instanceMethod method will only accept a value of type number (or any).

f.instanceMethod(123);   // OK
f.instanceMethod('123'); // Compile error
like image 115
Fenton Avatar answered Sep 20 '22 11:09

Fenton


The point here is, that static generic template is not related to class (and therefore instance) template. So we just have to distinguish them (as in C# I'd say)

// generic here is T
class Foo<T>
{
    public TheT: T;
    constructor(t: T)
    {
        this.TheT = t;
    }

    // the static one is U
    public static factory<U>(item: U): Foo<U>
    {
        var result = new Foo<U>(item);
        // the U here will be T inside of the instance
        return result;
    }
}

and we can call it like:

var f = Foo.factory(<Number> 1);
like image 22
Radim Köhler Avatar answered Sep 18 '22 11:09

Radim Köhler