Infer Keyword in TypeScript

The infer keyword captures a piece of a type during a conditional check so you can use it in the true branch. Learn how to extract return types, array elements, and promise values.

7 min read

The TypeScript infer keyword captures a type variable from a position inside a type pattern. You use it inside the extends clause of a conditional type, and TypeScript fills in the captured type based on the actual type being checked.

Think of it as declaring a variable that TypeScript solves for. You write the shape you want to match, put infer where you want to capture a piece, and TypeScript extracts that piece:

typescripttypescript
type ElementType<T> = T extends Array<infer Item> ? Item : never;
 
type A = ElementType<string[]>;
// string
 
type B = ElementType<number[]>;
// number
 
type C = ElementType<boolean>;
// never

The infer position sits in the element slot of the array pattern. When the input is string[], TypeScript deduces that the captured type must be string and returns it in the true branch. When the input is not an array at all, the false branch returns never.

Why infer instead of indexed access

You can extract array element types with an indexed access (T[number]) instead of infer. Both work for simple cases, but infer scales better to complex structures.

Consider extracting a function return type. There is no clean way to express "the position after the arrow" with indexed access. Naming that position with infer R is clearer and more maintainable than any workaround.

For extracting the return type of any function, the pattern is simple:

typescripttypescript
type MyReturnType<T> = T extends (...args: never[]) => infer R ? R : never;
 
function greet(name: string): string {
  return `Hello, ${name}`;
}
 
type GreetReturn = MyReturnType<typeof greet>;
// string

The ...args: never[] part says "match any function regardless of parameters." The infer R captures whatever type sits in the return position. This is the same pattern the built-in ReturnType utility uses.

Extracting function parameters and tuple parts

Flip the infer position to capture parameters instead. The built-in Parameters utility type works this way.

Infer also works naturally with tuple patterns. Capture the first element of any tuple:

typescripttypescript
type First<T> = T extends [infer F, ...unknown[]] ? F : never;
 
type A = First<[string, number, boolean]>;
// string

You can use multiple infer variables in the same pattern to capture the head and tail of a tuple, specific positions, or the last element.

Extracting promise values

The built-in Awaited type uses infer recursively to unwrap promise chains. Each level peels off one wrapper:

typescripttypescript
type MyAwaited<T> = T extends PromiseLike<infer U> ? MyAwaited<U> : T;
 
type Val = MyAwaited<Promise<Promise<number>>>;
// number

The infer captures the resolved value type, and the recursive call handles nested promises. For more on recursive type patterns, see Recursive Types in TypeScript.

Extracting from template literal types

Infer works inside template literal types to parse string patterns at the type level:

typescripttypescript
type EventBase<T extends string> = T extends `${infer Base}Changed`
  ? Base
  : never;
 
type Name = EventBase<"firstNameChanged">;
// "firstName"

The infer captures whatever text appears before the literal Changed suffix.

Adding constraints to inferred types

Since TypeScript 4.7, you can add an extends constraint directly on the inferred variable. The true branch only runs when the captured type satisfies the constraint:

typescripttypescript
type FirstIfString<T> = T extends [infer S extends string, ...unknown[]]
  ? S
  : never;
 
type A = FirstIfString<["hello", number]>;
// "hello"
 
type B = FirstIfString<[42, number]>;
// never

Without the constraint, the first example would return 42. With extends string added to the infer variable, the true branch only applies when the first element is string-compatible.

How distribution interacts with infer

When a conditional type with infer receives a union, distribution happens first, and infer captures from each member individually:

typescripttypescript
type ElementTypes<T> = T extends Array<infer Item> ? Item : never;
 
type Result = ElementTypes<string[] | number[]>;
// string | number

TypeScript distributes over string[] and number[] separately, capturing string from one and number from the other, then unions the results. For more on distribution, see Distributive Conditional Types in TypeScript.

Common mistakes

Using infer outside a conditional type is a syntax error. Infer is only valid inside the extends clause.

Expecting infer to work at runtime is another mistake. Like all TypeScript types, infer exists only at compile time and produces no runtime behavior.

When inferring from a type with multiple call signatures (an overloaded function), TypeScript uses the last -- most general -- signature. You cannot do overload resolution at the type level based on argument types.

Rune AI

Rune AI

Key Insights

  • infer captures a type variable inside a conditional type's extends clause.
  • Use infer for return types, array elements, promise values, and tuple parts.
  • Place infer in the position you want to capture.
  • Since TypeScript 4.7, infer R extends Constraint adds a constraint to the inferred type.
  • The built-in ReturnType, Parameters, and Awaited types all use infer internally.
RunePowered by Rune AI

Frequently Asked Questions

What does the infer keyword do in TypeScript?

The `infer` keyword declares a type variable inside the `extends` clause of a conditional type. It captures part of the matched type so you can use that captured type in the true branch.

Where can I use the infer keyword?

`infer` can only be used inside the `extends` clause of a conditional type. It cannot appear outside of that position.

Conclusion

The infer keyword lets you capture pieces of types inside conditional checks. Instead of manually indexing into a type, you declare an infer variable and TypeScript fills it in based on the pattern you match. It powers most of TypeScript's built-in utility types and is the key to writing reusable type transformations.