Concept 7 of 12
infer
infer is pattern matching. You describe the shape you expect, put infer X where the interesting part sits, and the compiler solves for X. Most of the standard library’s extraction helpers are one conditional with one infer in the right place.
- Expression
Item<string[]> - Resolves to
string - Expression
Item<number[]> - Resolves to
number - Expression
Item<boolean> - Resolves to
never
Match an array, capture its element type as U. A non-array does not match this pattern.
Type definition
type Item<T> = T extends (infer U)[] ? U : neverIt works anywhere a type can appear
type Unwrap<T> = T extends Promise<infer Inner> ? Inner : T type Element<T> = T extends (infer E)[] ? E : never type Returns<T> = T extends (...a: never[]) => infer R ? R : never type Head<T> = T extends [infer H, ...unknown[]] ? H : never type Rest<S> = S extends `${string}-${infer R}` ? R : never
Promises, arrays, function returns, tuple positions, string patterns — the mechanism is identical every time. Only the surrounding shape changes, which is why learning one of these teaches you all of them.
Constraining what gets bound
// Without the constraint, F is unknown and cannot be // interpolated into a template literal. type First<T> = T extends [infer F extends string, ...unknown[]] ? `${F}!` : never
An infer can carry its own extends clause, which narrows what it binds and lets you use the result where a specific kind of type is required. Without it you often get unknown, and the error appears one line later than the cause.
The common wrong answer
// Intent: get the return type of any function. type Returns<T> = T extends () => infer R ? R : never Returns<() => string> // string Returns<(a: number) => string> // never — pattern demands zero parameters // Fixed: never[] in parameter position accepts any signature, // because parameters are checked contravariantly. type Returns2<T> = T extends (...a: never[]) => infer R ? R : never
The pattern has to be as permissive as the inputs you want to match. A zero-parameter function type only matches zero-parameter functions, so everything else silently falls into the false branch.
Exercise
Pull the element type out of an array type.
Try it
2ElementOf<string[]>—→ stringElementOf<number>—→ never
Takeaway
Describe the shape, mark the hole with infer, and let the compiler fill it. If nothing matches, the pattern is stricter than the input — not the other way round.

