Search code examples
genericsfunctional-programmingtypescripttype-systems

Why do type parameters disappear in TypeScript?


It seems like TypeScript is not capable of expressing a function which takes a generic function as a parameter and returns a generic function. Note the generic type <A> becoming {} in the following code sample with the function doubleton:

function compose<A, B, C>(
        g: (b: B) => C,
        f: (a: A) => B):  (a: A) => C {
    return a => g(f(a));
}

function singleton<A>(a: A): A[] { return [a]; }

// declare var doubleton: (a: {}) => {}[];
var doubleton = compose(singleton, singleton);

Oh no! We lost our type parameter A.

var x = 1; // number
var xs = singleton(x); // number[]
var xss = doubleton(x); // {}[]

And now we've seen the effects of doubleton's loss of genericity. Because of it, number became {}.

Is there some way to specify make compose preserve these type parameters, other than defining doubleton as follows?

function doubleton<A>(a: A) { return [[a]]; }

I know it seems trivial in this example, but I've run into more complex return-type cases (such as auto-curried functions) where it is non-trivial to reiterate yourself.


Solution

  • The issue seems to be in the way type information of the form of function signatures interact with generic parameters. e.g. a simpler example (based on yours) that fails :

    function mirror<A, B>(f: (a: A) => B)
        : (a: A) => B {
        return f;
    }
    
    function singleton<A>(a: A): A[] { return [a]; }
    
    // (a:{})=>{}[]
    var singletonMirror = mirror(singleton);
    
    var x = 1;
    var xs = singleton(x); // number[]
    var xsM = singletonMirror(x); // {}[]
    

    Will work if you do not ask the type system to infer A and B from f:

    function mirror<T>(f: T)
        : T {
        return f;
    }
    
    function singleton<A>(a: A): A[] { return [a]; }
    
    // okay
    var singletonMirror = mirror(singleton);
    
    var x = 1;
    var xs = singleton(x); // number[]
    var xsM = singletonMirror(x); // number[]
    

    I would report it as an issue : https://github.com/microsoft/typescript/issues

    A further simplification of the error:

    function unity<A>(f: (a: A) => A)
        : (a: A) => A {
        return f;
    }
    
    function mirror<T>(a: T): T { return a; }
    
    // (a:{})=>{}, should be (a:T)=>T
    var unityMirror = unity(mirror);