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.
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:
type ElementType<T> = T extends Array<infer Item> ? Item : never;
type A = ElementType<string[]>;
// string
type B = ElementType<number[]>;
// number
type C = ElementType<boolean>;
// neverThe 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:
type MyReturnType<T> = T extends (...args: never[]) => infer R ? R : never;
function greet(name: string): string {
return `Hello, ${name}`;
}
type GreetReturn = MyReturnType<typeof greet>;
// stringThe ...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:
type First<T> = T extends [infer F, ...unknown[]] ? F : never;
type A = First<[string, number, boolean]>;
// stringYou 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:
type MyAwaited<T> = T extends PromiseLike<infer U> ? MyAwaited<U> : T;
type Val = MyAwaited<Promise<Promise<number>>>;
// numberThe 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:
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:
type FirstIfString<T> = T extends [infer S extends string, ...unknown[]]
? S
: never;
type A = FirstIfString<["hello", number]>;
// "hello"
type B = FirstIfString<[42, number]>;
// neverWithout 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:
type ElementTypes<T> = T extends Array<infer Item> ? Item : never;
type Result = ElementTypes<string[] | number[]>;
// string | numberTypeScript 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
Key Insights
infercaptures a type variable inside a conditional type'sextendsclause.- Use
inferfor return types, array elements, promise values, and tuple parts. - Place
inferin the position you want to capture. - Since TypeScript 4.7,
infer R extends Constraintadds a constraint to the inferred type. - The built-in
ReturnType,Parameters, andAwaitedtypes all useinferinternally.
Frequently Asked Questions
What does the infer keyword do in TypeScript?
Where can I use the infer keyword?
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.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.