C.W.K.
Stream
Lesson 07 of 07 · published

`infer`: Pulling Types Out of Other Types

~11 min · generics, infer, conditional-types, type-extraction

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"infer captures a type from a pattern match. The compiler reads the shape and binds the slot."

What infer does

infer is a keyword used only inside the extends clause of a conditional type. It declares a type variable that captures part of the pattern being matched. T extends Array<infer E> ? E : never says: "if T is some Array<E>, set E to that element type; otherwise never."

This is the type-level version of destructuring. You write a pattern, and infer X is the binding that captures whatever was in that position.

The canonical uses

Element of array: type ElementOf<T> = T extends (infer E)[] ? E : never. Extract the array's element type.

Return type of function: type ReturnOf<F> = F extends (...args: any[]) => infer R ? R : never. The standard library's ReturnType is exactly this.

Promised type: type Unwrap<T> = T extends Promise<infer V> ? V : T. Extract the value from a Promise.

First argument: type FirstArg<F> = F extends (first: infer A, ...rest: any[]) => any ? A : never. Pull out the first parameter type.

Why infer matters

Without infer, you can check a type's shape (T extends Array<unknown>) but you can't get any new information out of it. infer is what turns conditional types from yes/no questions into pattern matches that bind variables. It's the difference between asking 'is it an array?' and asking 'what's the element type?'

This is the gateway to type-level programming. Once you can extract types from other types, you can compose them, transform them, and build sophisticated derived types — all without runtime work.

infer plus conditional types plus generics is the full toolkit for type-level computation. Most of the advanced TypeScript patterns in the wild are some combination of these three.

Code

Four canonical infer patterns·typescript
// The canonical four `infer` patterns.

// 1. Element of array.
type ElementOf<T> = T extends (infer E)[] ? E : never;
type A = ElementOf<string[]>;        // string
type B = ElementOf<User[]>;          // User

// 2. Return type of function.
type ReturnOf<F> = F extends (...a: any[]) => infer R ? R : never;
type C = ReturnOf<() => string>;     // string
type D = ReturnOf<typeof JSON.parse>;// any (JSON.parse returns any)

// 3. Unwrap a Promise.
type Unwrap<T> = T extends Promise<infer V> ? V : T;
type E = Unwrap<Promise<User>>;      // User
type F = Unwrap<User>;               // User (non-Promise passes through)

// 4. First argument of function.
type FirstArg<F> = F extends (first: infer A, ...rest: any[]) => any ? A : never;
type G = FirstArg<(x: number, y: string) => void>;  // number

External links

Exercise

Write type ParametersOf<F> that extracts the tuple of parameter types from a function type. ParametersOf<(a: number, b: string) => void> should be [number, string]. Use infer to capture the rest parameters.
Hint
type ParametersOf<F> = F extends (...args: infer P) => any ? P : never. The ...args: infer P captures the entire parameter tuple at once.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.