TypeScriptinfer Keyword

The infer Keyword

The infer keyword is TypeScript's way of letting you capture a type from inside a conditional type's extends clause and refer to it in the true branch. Think of it as a type-level variable that TypeScript fills in automatically during pattern matching.

Without infer, conditional types can only test whether something matches a shape. With infer, they can also extract parts of that shape.

The Problem infer Solves

Suppose you want a type that extracts the return type of a function. Without infer you cannot do this generically, because you have no way to refer to "whatever the return type happens to be":

TS
// Without infer — you can only check, not extract
type ReturnsString<T> = T extends (...args: any[]) => string ? true : false;

// There is no way to say "give me the actual return type" without infer
// The following would be a type error:
// type BadReturnType<T> = T extends (...args: any[]) => ??? ? ??? : never;
Note
infer solves this by introducing a placeholder variable inside the extends clause that TypeScript infers from the matched type.
infer Syntax

The syntax is:

T extends SomeType<infer U> ? U : FallbackType

When T matches SomeType&lt;..., TypeScript binds whatever filled that slot to U. You can then use U in the true branch.

TS
// infer R captures the return type of any function signature
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type Fn1 = () => string;
type Fn2 = (x: number, y: number) => boolean;
type Fn3 = (name: string) => { id: number; name: string };

type R1 = ReturnType<Fn1>; // string
type R2 = ReturnType<Fn2>; // boolean
type R3 = ReturnType<Fn3>; // { id: number; name: string }
type R4 = ReturnType<string>; // never (string is not a function)
Tip
This is exactly how TypeScript's built-in ReturnType<T> utility works. You can inspect it yourself in lib.es5.d.ts.
Extracting Return Types Manually vs ReturnType

Before infer existed (TypeScript 2.8+), extracting a function's return type required workarounds. Today the built-in ReturnType handles it — but building it yourself is the best way to understand infer:

TS
// Manual implementation — identical to the built-in
type MyReturnType<T extends (...args: any[]) => any> =
  T extends (...args: any[]) => infer R ? R : never;

function greet(name: string): string {
  return `Hello, ${name}!`;
}

function add(a: number, b: number): number {
  return a + b;
}

type GreetReturn = MyReturnType<typeof greet>; // string
type AddReturn   = MyReturnType<typeof add>;   // number

// Built-in equivalent
type GreetReturn2 = ReturnType<typeof greet>; // string
Unwrapping Promises

infer is perfect for stripping wrappers from types. Unwrapping a Promise is a classic use case — and TypeScript 4.5 introduced Awaited<T> as the built-in, but writing it yourself is instructive:

TS
// Single-level unwrap
type UnwrapPromise<T> = T extends Promise<infer V> ? V : T;

type P1 = UnwrapPromise<Promise<string>>;  // string
type P2 = UnwrapPromise<Promise<number[]>>; // number[]
type P3 = UnwrapPromise<boolean>;           // boolean (not a Promise, returns T)

// Recursive unwrap — handles Promise<Promise<...>>
type DeepAwaited<T> =
  T extends Promise<infer V>
    ? DeepAwaited<V>
    : T;

type Nested = DeepAwaited<Promise<Promise<Promise<string>>>>; // string

// TypeScript 4.5+ built-in (handles PromiseLike, thenables, etc.)
type A1 = Awaited<Promise<string>>;                     // string
type A2 = Awaited<Promise<Promise<number>>>;            // number
Extracting Array Element Types

You can use infer to pull out the element type of any array — a common need when building generic utilities that operate on array items:

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

type E1 = ElementType<string[]>;          // string
type E2 = ElementType<number[]>;          // number
type E3 = ElementType<Array<boolean>>;    // boolean
type E4 = ElementType<string>;            // never (not an array)

// Also works with readonly arrays
type ElementTypeR<T> = T extends readonly (infer E)[] ? E : never;

type E5 = ElementTypeR<readonly string[]>; // string
type E6 = ElementTypeR<readonly [1, 2, 3]>; // 1 | 2 | 3

// Built-in equivalent: ArrayElement is not built-in, but you can use:
type E7 = string[][number]; // string  (indexed access type — another approach)
Note
For readonly arrays you must use readonly (infer E)[] as the pattern, because readonly string[] does not extend the mutable (infer E)[].
Extracting Function Parameter Types

Just as you can extract return types, you can extract parameter types. TypeScript's built-in Parameters<T> uses this exact technique:

TS
// Extract all parameters as a tuple
type MyParameters<T extends (...args: any[]) => any> =
  T extends (...args: infer P) => any ? P : never;

function createUser(name: string, age: number, admin: boolean): void {}

type Params = MyParameters<typeof createUser>;
// [name: string, age: number, admin: boolean]

type FirstParam = Params[0]; // string
type SecondParam = Params[1]; // number

// Extract only the first parameter
type FirstArg<T extends (...args: any[]) => any> =
  T extends (first: infer F, ...rest: any[]) => any ? F : never;

type F1 = FirstArg<typeof createUser>; // string

// Extract the last parameter
type LastArg<T extends (...args: any[]) => any> =
  T extends (...args: [...infer _, infer L]) => any ? L : never;

type L1 = LastArg<typeof createUser>; // boolean
Extracting from Object Types

infer is not limited to functions and arrays. You can use it to extract property types from object shapes:

TS
// Extract the value type of a specific key
type ValueOf<T, K extends keyof T> = T extends { [P in K]: infer V } ? V : never;

interface User {
  id: number;
  name: string;
  roles: string[];
}

type IdType   = ValueOf<User, "id">;    // number
type NameType = ValueOf<User, "name">;  // string
type RoleType = ValueOf<User, "roles">; // string[]

// Extract constructor parameter types
type ConstructorParams<T extends new (...args: any[]) => any> =
  T extends new (...args: infer P) => any ? P : never;

class Point {
  constructor(public x: number, public y: number) {}
}

type PointParams = ConstructorParams<typeof Point>; // [x: number, y: number]
Nested infer Patterns

Multiple infer bindings can appear in a single extends clause. This lets you extract several parts of a type simultaneously:

TS
// Extract both the first argument and return type of a function
type FirstArgAndReturn<T> =
  T extends (first: infer A, ...rest: any[]) => infer R
    ? { arg: A; ret: R }
    : never;

type Fn = (name: string, age: number) => boolean;
type Info = FirstArgAndReturn<Fn>;
// { arg: string; ret: boolean }

// Unwrap nested generic: Extract<Promise<Array<T>>, T>
type UnwrapPromiseArray<T> =
  T extends Promise<(infer U)[]>
    ? U
    : never;

type PA = UnwrapPromiseArray<Promise<string[]>>;  // string
type PB = UnwrapPromiseArray<Promise<number[]>>;  // number
type PC = UnwrapPromiseArray<string[]>;           // never
Tip
You can have as many infer bindings in one pattern as you need. Each one captures a different structural position of the matched type.
Real-World Use Cases

Here are practical utility types from real TypeScript codebases, all powered by infer:

TS
// 1. Get the resolved value type of any thenable
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;

// 2. Flatten one level of array nesting
type Flatten<T> = T extends Array<infer Item> ? Item : T;

type F = Flatten<string[][]>; // string[]  (one level removed)

// 3. Get the type a React ref holds
type RefValue<T> = T extends React.RefObject<infer V> ? V : never;

// 4. Extract event handler payload
type EventPayload<T> = T extends (event: infer E) => void ? E : never;

type Handler = (event: MouseEvent) => void;
type Payload = EventPayload<Handler>; // MouseEvent

// 5. Infer keys and values from a Map
type MapKey<T>   = T extends Map<infer K, any> ? K : never;
type MapValue<T> = T extends Map<any, infer V> ? V : never;

type M = Map<string, number>;
type K = MapKey<M>;   // string
type V = MapValue<M>; // number
infer with Union Inference

When infer can match a type in multiple positions, TypeScript unions the candidates for co-variant positions and intersects them for contra-variant positions (like function parameters).

TS
// Co-variant position — TypeScript unions the candidates
type ElementsOf<T> = T extends { a: infer U; b: infer U } ? U : never;

type E = ElementsOf<{ a: string; b: number }>; // string | number

// Contra-variant position — TypeScript intersects the candidates
type IntersectFnArgs<T> =
  T extends { a: (x: infer U) => void; b: (x: infer U) => void }
    ? U
    : never;

type I = IntersectFnArgs<{
  a: (x: string) => void;
  b: (x: number) => void;
}>; // string & number  (i.e. never)
Warning
Relying on contra-variant union inference (intersection of function parameters) can produce never. This is correct TypeScript behaviour — functions must be callable with the same argument type, so conflicting types intersect to never.
Summary — What infer Can Extract

Pattern

Extracts

T extends (...args: any[]) => infer R

Return type of a function

T extends (...args: infer P) => any

Parameter tuple of a function

T extends Promise<infer V>

Resolved value of a Promise

T extends (infer E)[]

Element type of an array

T extends Map<infer K, infer V>

Key and value types of a Map

T extends new (...args: infer P) => any

Constructor parameter tuple

Success
You can now use infer to extract return types, parameters, array elements, Promise values, and more. Combined with conditional types, infer is the foundation of nearly every advanced TypeScript utility type.