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[]>; // numberYou 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
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.
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.
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>; // neverAwaited β unwrap Promise
Recursively unwraps nested Promises by inferring the resolved value V at each level, matching the built-in Awaited<T>.
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