ESC

Type to search the knowledge base.

infer Keyword Basics

Use infer inside conditional types to extract return types, promise payloads, array elements, and function parameters.

advanced3 min read
  • typescript
  • infer-keyword

infer appears only in conditional types. It declares a type variable to be inferred from a matched shape — “if T looks like this, bind that piece to R.”

type ReturnOf<F> = F extends (...args: never[]) => infer R ? R : never;

type R = ReturnOf<() => number>; // number

Docs: Conditional Types — infer.

Why not always use built-ins?

Prefer stdlib when it exists:

Built-in Role
ReturnType<F> Function return
Parameters<F> Parameter tuple
ConstructorParameters<C> Constructor args
InstanceType<C> Instance of constructor
Awaited<T> Unwrap promises recursively

Learn infer to build the next helper those don’t cover, and to read library types.

Extract array element

type Elem<T> = T extends readonly (infer E)[] ? E : never;

type A = Elem<string[]>; // string
type B = Elem<readonly number[]>; // number

Unwrap Promise

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

type U = Unwrap<Promise<User>>; // User

Recursive (like Awaited):

type DeepAwait<T> = T extends Promise<infer U> ? DeepAwait<U> : T;

Function parameters

type Params<F> = F extends (...args: infer P) => unknown ? P : never;

type P = Params<(a: string, b: number) => void>; // [string, number]

Object property by pattern

type PropType<T, K extends PropertyKey> = T extends Record<K, infer V> ? V : never;

type N = PropType<{ age: number }, 'age'>; // number

Multiple infer positions

type MapOrigin<T> = T extends Map<infer K, infer V> ? [K, V] : never;

type Pair = MapOrigin<Map<string, User>>; // [string, User]

Distributive behavior

Bare T in T extends … infer … distributes over unions:

type UnwrapArray<T> = T extends (infer E)[] ? E : T;

type X = UnwrapArray<string[] | number[]>; // string | number

Wrap in a tuple to block distribution when needed: [T] extends [(infer E)[]] ? E : T.

Practical frontend helper

type EventProp<T> = T extends `on${string}`
  ? T extends keyof React.JSX.IntrinsicElements['button']
    ? never
    : T
  : never;

// More useful: extract payload from a typed action map
type ActionMap = {
  login: { userId: string };
  logout: undefined;
  setTitle: { title: string };
};

type PayloadOf<T extends keyof ActionMap> = ActionMap[T];

// With infer from a handler type:
type HandlerPayload<H> = H extends (payload: infer P) => void ? P : never;

Reading error messages

When inference fails, the false branch (never or fallback) appears. Split aliases:

type Step = MyFn extends (...a: infer P) => infer R ? [P, R] : never;

Hover Step in the IDE instead of debugging a 40-line compound type.

Footguns

  1. infer outside conditionals — syntax error.
  2. Using any[] in patterns — can weaken inference; prefer never[] or unknown[] carefully.
  3. Reimplementing ReturnType — use the built-in.
  4. Over-recursive infer — hit instantiation depth; simplify.
// Prefer
type R = ReturnType<typeof loadUser>;
// over hand-rolled infer for the same thing

Interview out-loud answer

“infer introduces a type variable inside a conditional type to capture part of a matched shape — return types, promise values, element types. Built-ins cover common cases; I write custom infer helpers for domain-specific extraction and watch distribution over unions.”

Further reading

Related guides