Search code examples
typescriptgenericstypescript-genericstypescript-declarations

How can I get the type of a function application in TypeScript?


How can I get the type of a generic function applied to a parameter in TypeScript?

For example, how could I define Apply below?

declare function f<T>(t: T): T extends number ? boolean : object;

type ResForNumArg = Apply<typeof f, number>;    // should be boolean
type ResForStringArg = Apply<typeof f, object>; // should be object

playground link

If there's no way to do this, one can hack around the problem for specific cases by making a type alias that is a type-level version of the function, like F below:

declare function f<T>(t: T): T extends number ? boolean : object;
type ApplyF<T> = T extends number ? boolean : object;

type ResForNumArg = ApplyF<number>;    // boolean
type ResForStringArg = ApplyF<object>; // object

But ApplyF can fall out of sync with f and is annoying to type. Is there a better way?

Update: this seems to be related to https://github.com/microsoft/TypeScript/issues/29043


Solution

  • As you correctly have found it is not possible to use function declaration as generic type, its not possible to apply the generic without function execution. We can only apply generic during calling of the function (or will be infered from the argument):

    const r1 = f<number>(1) //  boolean
    const r2 = f(1) // boolean
    

    Ok so we understand it is not possible. Now the workaround, in order to have it working without loosing connection with original declaration I propose additional generic type FType. Consider:

    type FType<T> = (t: T) => T extends number ? boolean : object;
    // join function declaration with FType:
    declare function f<T>(...t: Parameters<FType<T>>): ReturnType<FType<T>>
    
    type ResForNumArg =  ReturnType<FType<number>>;    // bool 
    type ResForStringArg = ReturnType<FType<object>>;  // object
    

    By using utility types Parameters and ReturnType I connected FType with function f declaration. Its verbose but we get what we want finally and FType can be applied in the standard way.

    Playground link