The infer Keyword

Extracting a Type From Inside Another

Inside the extends clause, infer X declares a type variable that TypeScript fills in by pattern matching. This is how ReturnType, Parameters and Awaited are built.

type MyReturnType<F> = F extends (...args: any[]) => infer R ? R : never;
type MyParameters<F> = F extends (...args: infer P) => any ? P : never;
type UnwrapPromise<T> = T extends Promise<infer V> ? UnwrapPromise<V> : T;
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
type FirstArg<F> = F extends (first: infer A, ...rest: any[]) => any ? A : never;

Using infer

Example

The error shows the inferred types: R is { id: string; total: number } and V is number.

type MyReturnType<F> = F extends (...args: any[]) => infer R ? R : never;
type UnwrapPromise<T> = T extends Promise<infer V> ? UnwrapPromise<V> : T;

function createOrder(total: number) {
  return { id: "OD-1", total };
}

type Order = MyReturnType<typeof createOrder>;
type Price = UnwrapPromise<Promise<Promise<number>>>;

const o: Order = { id: "OD-2" };
const p: Price = "499";

Compiler output (tsc)

Line 11: error TS2741: Property 'total' is missing in type '{ id: string; }' but required in type '{ id: string; total: number; }'.
Line 12: error TS2322: Type 'string' is not assignable to type 'number'.

infer in Practice

Example

Extract the type of the data field from any API response type, and the element type of a paginated list.

type ApiResponse<T> = { status: number; data: T };
type DataOf<R> = R extends ApiResponse<infer D> ? D : never;
type ItemOf<R> = DataOf<R> extends { items: (infer I)[] } ? I : never;

type ProductsResponse = ApiResponse<{ items: { id: number; name: string }[]; total: number }>;

type ProductsData = DataOf<ProductsResponse>;   // { items: ...; total: number }
type ProductItem = ItemOf<ProductsResponse>;    // { id: number; name: string }

const data: ProductsData = { items: [{ id: 1, name: "Kettle" }], total: 1 };
const item: ProductItem = data.items[0];
console.log(item.name, data.total);

Output

Kettle 1