Skip to content

Inference & infer

Where inference gets its answers: inference sites

Section titled “Where inference gets its answers: inference sites”

When you call a generic function without type arguments, TypeScript looks at every place the type parameter appears in the parameters — each is an inference site — and computes a type that fits them all.

function pair<A, B>(a: A, b: B): [A, B] {
return [a, b];
}
const p = pair(1, "hi");
// ^? [number, string] — A from the first arg, B from the second

When a parameter appears more than once, TypeScript reconciles the candidates:

function both<T>(a: T, b: T): T { return a; }
const x = both(1, 2); // T = number
const y = both(1, "two"); // T = number | string (best common type)

Understanding that inference reads argument positions explains most “why did it infer that?” moments — and why annotating a return type never changes what the arguments infer.

infer: extract a type from inside another type

Section titled “infer: extract a type from inside another type”

Inside a conditional type, the infer keyword introduces a new type variable that TypeScript solves for you by pattern-matching the structure. It is how you pull a piece out of a bigger type.

The canonical example — get a function’s return type:

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type A = MyReturnType<() => number>; // number
type B = MyReturnType<(x: string) => User>; // User
type C = MyReturnType<string>; // never (not a function)

Read infer R as: “match this type against the pattern (...args) => R, and if it fits, bind R to whatever was in the return position.” This is exactly how the built-in ReturnType<T> is defined.

The pattern generalizes: pull out any part

Section titled “The pattern generalizes: pull out any part”

infer can capture any position in a structure — element types, promise values, argument types:

// element type of an array
type ElementType<T> = T extends (infer E)[] ? E : T;
type E1 = ElementType<number[]>; // number
type E2 = ElementType<string>; // string (unchanged — not an array)
// the value a Promise resolves to
type Awaited1<T> = T extends Promise<infer V> ? V : T;
type V1 = Awaited1<Promise<string>>; // string
// the first parameter type
type FirstArg<T> = T extends (first: infer P, ...rest: any[]) => any ? P : never;
type P1 = FirstArg<(id: number) => void>; // number

Each one is the same move: describe the shape you expect, put infer X where the interesting part lives, and TypeScript fills X in when the shape matches.

A pitfall: multiple infer in the same position

Section titled “A pitfall: multiple infer in the same position”

When the same infer name appears in multiple spots, TypeScript’s behavior depends on position — it infers a union in covariant positions and an intersection in contravariant (parameter) positions. You rarely need this, but it explains occasional surprises:

type Merge<T> = T extends { a: infer U; b: infer U } ? U : never;
type M = Merge<{ a: string; b: number }>; // string | number (union)

For most day-to-day work, one infer per pattern is all you need.

Where does TypeScript get the values for a generic function's type parameters?
What does `infer R` do in `T extends (...args: any[]) => infer R ? R : never`?
How would you extract the element type from an array type?
The built-in `ReturnType<T>` is built using: