Inferring Types with infer

Capture a piece of a type from inside a conditional and reuse it.

The infer keyword lives inside a conditional type's extends clause. It says "there is a type here β€” bind it to a name so I can use it in the true branch." It is how you pull a type out of a larger structure.

type ElementType<T> = T extends (infer E)[] ? E : never;
type X = ElementType<number[]>; // number

You can infer almost any position β€” a return type, a promise's resolved value, a function parameter, a tuple's head:

type ReturnOf<F> = F extends (...args: any[]) => infer R ? R : never;
type Awaited1<T> = T extends Promise<infer V> ? V : T;

This is the exact machinery behind built-ins like ReturnType, Parameters, and Awaited. Understanding infer demystifies the whole standard library of utility types.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • A ReturnType utility captures the return type of any function without the caller needing to specify it manually.
  • A FirstArgument utility infers the type of a function's first parameter from its signature.
  • An Awaited utility recursively infers the resolved value type from a nested Promise.

More examples

Infer function return type

infer R captures the return type from the function signature, reproducing the built-in ReturnType utility type.

Example Β· ts
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function greet(name: string): string { return `Hello, ${name}`; }
async function fetchId(): Promise<number> { return 1; }

type A = ReturnType<typeof greet>;     // string
type B = ReturnType<typeof fetchId>;   // Promise<number>

Infer first parameter type

Captures the first parameter type A via infer and returns never when the function has no parameters.

Example Β· ts
type FirstArg<T> = T extends (first: infer A, ...rest: any[]) => any ? A : never;

type A = FirstArg<(id: number, name: string) => void>; // number
type B = FirstArg<() => void>;                          // never

Awaited β€” unwrap Promise

Recursively unwraps nested Promises by inferring the resolved value V at each level, matching the built-in Awaited<T>.

Example Β· ts
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T;

type A = Awaited<Promise<string>>;            // string
type B = Awaited<Promise<Promise<number>>>;   // number
type C = Awaited<boolean>;                    // boolean

Discussion

  • Be the first to comment on this lesson.
Inferring Types with infer β€” TypeScript | SoundsCode