infer

Infer inside conditions

infer declares a fresh type variable inside a condition and captures whatever sits in its spot. Think of it as pattern matching for types.

First matches an array of something, names that something E, and returns it. A non-array falls through to never.

type First<T> = T extends Array<infer E> ? E : never; type F = First<Array<string>>; // string

Infer return types

Return positions work the same way. Match the function shape, capture the result with infer R, and return it.

This mirrors the built-in ReturnType, where the any array accepts every argument list. N resolves to string for a function that returns a string.

type Returns<F> = F extends (...args: any[]) => infer R ? R : never; type N = Returns<(id: number) => string>; // string

Infer arguments

Parameter positions can be captured too. Match the first parameter with infer A and let the rest collect the remainder.

S resolves to string because the example takes a name first. This is the same idea behind the built-in Parameters helper.

type FirstArg<F> = F extends (first: infer A, ...rest: unknown[]) => unknown ? A : never; type S = FirstArg<(name: string) => number>; // string

Unwrap promises

Promises nest, and infer can peel every layer with recursion. If the type is a Promise, capture the inside and unwrap again, else stop.

U resolves to plain string no matter how deep the nesting goes. The built-in Awaited type follows the same recipe.

type Unwrap<T> = T extends Promise<infer V> ? Unwrap<V> : T; type U = Unwrap<Promise<Promise<string>>>; // string

Next: Template literal types

Article author: Arthur Isaev