TypeScript how to create a generic type alias for a generic function?

UPDATE FOR TS4.7+

Hello again! TypeScript 4.7 will introduce instantiation expressions as implemented in microsoft/TypeScript#47607, which will allow you to directly specify the type parameters for a generic function without actually calling the function, so there’s no longer a need to abuse classes the way shown in the rest of the answer. Here it is:

type GFooT<T,> = typeof foo<T>  // (arg: T) => T
type GFooParametersT<T,> = Parameters<typeof foo<T>>  // [arg: T]
type GFooReturnT<T,> = ReturnType<typeof foo<T>>  // T

That’s almost the same as the syntax in the question except that trailing commas are not supported after type arguments (but they are supported after type parameter declarations). Hooray!

Playground link to code


PREVIOUS ANSWER FOR TS4.6-

There are two different flavors of generics in TypeScript: generic functions, and generic types… and it looks like you want the compiler to transform one into the other for you, which isn’t directly supported.


To be clear:

Generic types have type parameters that need to be specified before you can use them as a specific type. For example:

type GenType<T> = (x: T) => T[];
declare const oops: GenType; // error
declare const genT: GenType<string>; // okay
const strArr = genT("hello"); // string[];
const numArr = genT(123); // error!

Here, GenType is a generic type. You need to specify the type parameter to use it as the type of a value, and then the resulting type is no longer generic. The genT function takes a string and returns a string[]. It cannot be used as a function that takes a number and returns a number[].


Generic functions, on the other hand, have a specific type that can act like any possible substitution of its type parameters. The value of a generic function type is still generic when you use it. The type parameter is attached to the call signature:

type GenFunc = <T>(x: T) => T[];
declare const genF: GenFunc;
const strArr = genF("hello"); // strArr: string[];
const numArr = genF(123); // numArr: number[];

Here, GenFunc is a specific type referring to a generic function. The genF function is still generic when it is called.

Generic functions (including generic constructor functions) can be thought of as generic values, as opposed to generic types.


These two flavors of generics are related to each other but the TypeScript type system isn’t expressive enough to talk about how they are related. In some other language, you might be able to define one in terms of the other like

type GenFunc = forall T, GenType<T>; // not TS, error

or

type GenType<T> = instantiate GenFunc with T; // not TS, error

but in TypeScript you can’t. Maybe if we ever get higher kinded types as requested in microsoft/TypeScript#1213… but not now. So it is not directly possible to turn GenFunc into GenType programmatically in the type system.


There are evil awful ways to coerce the compiler to calculate GenType in terms of GenFunc. The way I know of makes use of generic class property initialization and some higher order type inference for generic functions introduced in TypeScript 3.4. I’m making the compiler think it’s calculating values when I don’t actually have any, and then getting the type of one of these pretend values:

class GenTypeMaker<T> {
    getGenType!: <A extends any[], R>(cb: (...a: A) => R) => () => (...a: A) => R;
    genType = this.getGenType(null! as GenFunc)<T>()
}
type GenType2<T> = GenTypeMaker<T>['genType']
// type GenType2<T> = (x: T) => T[]

You can verify that GenType2<T> is the same type as GenType<T>, and if you change GenFunc into any generic function with one type parameter, GenType2<T> will change accordingly.

But I don’t know I’d want to recommend anyone actually use this method. And it doesn’t really scale or compose; if you have an object full of generic functions in one type parameter and you want to transform it to an object full of specific functions with a specified type parameter, there’s no way to use this method to get it from the type system without doing it once for each object property.


Playground link to code

Leave a Comment