Suggest an editImprove this articleRefine the answer for “Infer keyword in TypeScript — infer TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`infer`** is a TypeScript keyword that captures a type from inside a conditional type match. ```typescript type FnReturn<T> = T extends (...args: any[]) => infer R ? R : never; type Result = FnReturn<() => string>; // string type NotFn = FnReturn<number>; // never ``` **Key point:** `infer R` binds to whatever type occupies that structural position when the `extends` match succeeds. The captured type is only available in the true branch.Shown above the full answer for quick recall.Answer (EN)Image**`infer`** is a TypeScript keyword that captures and names a type from inside a conditional type match, letting you pull out parts of a type structure without knowing them in advance. ## Theory ### TL;DR - Think of `infer` as a regex capture group for types: you describe the shape, TypeScript fills in the matched part - Works only inside the `extends` clause of a conditional type - The captured type is available only in the true branch - Use it when a nested type repeats across your API and you want to extract it once - Skip it when the type is already known: use indexed access `T['key']` instead ### Quick example ```typescript // Extract return type from any function type FnReturn<T> = T extends (...args: any[]) => infer R ? R : never; type AddReturn = FnReturn<(a: number, b: number) => number>; // number type StringReturn = FnReturn<() => string>; // string type NotAFn = FnReturn<string>; // never ``` The `infer R` part tells TypeScript: "whatever sits in the return position, capture it as R." If the match succeeds, `R` is the return type. If it fails, the false branch returns `never`. ### How `infer` differs from direct type access Direct access like `T['returnType']` works when the property name is known. `infer` works when the structure is known but the inner type is not. You can pattern-match a function signature, an array, or a Promise and extract the type sitting inside, regardless of what that type turns out to be. That is the key difference: `infer` captures types from structural positions, not from named keys. Without `infer`, extracting a function's return type would require either hardcoding the type or passing it as a separate generic parameter. Both approaches break the moment the function is provided externally. ### When to use - Extract function return type: `T extends (...args: any[]) => infer R ? R : never` - Pull element type from an array: `T extends readonly (infer U)[] ? U : never` - Unwrap a Promise: `T extends Promise<infer U> ? U : never` - Capture function parameters as a tuple: `T extends (...args: infer A) => any ? A : never` - Avoid `infer` when the type is already known: use `T[0]` for a known tuple position, `T['value']` for a known property ### How the compiler handles `infer` TypeScript resolves `infer` during type-checking, not at runtime. When the compiler evaluates a conditional type, it tries to unify the input against the `extends` pattern. If the match succeeds, it binds `infer R` to the type that occupies that structural position, then substitutes `R` into the true branch. This all happens at compile time inside the type mapper, similar to how generic parameters are instantiated. No code is generated. No runtime overhead. The bound variable exists only within the true branch of that specific conditional. ### Common mistakes **Mistake 1: Using `infer` outside a conditional type** ```typescript // infer only exists inside an extends clause type Bad<T> = infer R; // Error: 'infer' declarations are only permitted in the 'extends' clause ``` `infer` is not a standalone keyword. It has no meaning outside the `extends` clause of a conditional type. **Mistake 2: Expecting the captured type in the false branch** ```typescript type Foo<T> = T extends { x: infer U } ? U : U; // Error - U is not in scope here ``` `U` exists only in the true branch. Once the match fails and TypeScript takes the false path, the captured variable is gone. **Mistake 3: Forgetting distribution over unions** ```typescript type ElementType<T> = T extends (infer U)[] ? U : never; type Result = ElementType<string[] | number[]>; // string | number ``` TypeScript distributes conditional types over unions, applying the conditional to each member separately and then combining the results. This is usually what you want, but it can surprise you when one union member does not match the pattern. **Mistake 4: Capturing function parameters by exact position instead of rest** ```typescript // Fragile: only matches functions with exactly two params type TwoArgReturn<T> = T extends (a: infer A, b: infer B) => infer R ? R : never; // Better: captures all params as a tuple regardless of count type Params<T> = T extends (...args: infer A) => any ? A : never; ``` Using `...args: infer A` gives you a tuple of all parameters in one shot. Matching positional parameters one by one only works if the function has a fixed, known signature. **Mistake 5: Expecting `infer` to do something at runtime** `infer` is erased completely during compilation. If you need to inspect or validate types at runtime, you need a different tool: `typeof` operators, type guards, or a schema library like Zod. ### Real-world usage - **Built-in utilities**: `ReturnType<T>`, `Parameters<T>`, and `Awaited<T>` are all built on `infer` in TypeScript's standard library - **React/Redux**: `ReturnType<typeof useSelector>` extracts the selector's return type, giving you the store slice type without writing it twice - **Zod**: `z.infer<typeof schema>` pulls the validated TypeScript type from a schema definition - **tRPC**: `inferRouterOutputs<AppRouter>` gives you typed API responses derived directly from your router - **TanStack Query**: hook generics use `infer`-based patterns to carry the data type through the query pipeline ### Follow-up questions **Q:** Write a type that extracts the arguments tuple from any function. **A:** `type Params<T> = T extends (...args: infer A) => any ? A : never;` The rest syntax `...args` captures all parameters as a single typed tuple, regardless of how many there are. **Q:** What does the false branch return when `infer` does not match? **A:** Whatever you put there explicitly, typically `never`. The `infer` variable simply does not exist in the false branch. There is nothing to fall back on. **Q:** How does `infer` behave when the input is a union type? **A:** TypeScript distributes the conditional over each union member separately. `ElementType<string[] | number[]>` evaluates `ElementType<string[]>` and `ElementType<number[]>` independently, then unions the results to give `string | number`. **Q:** Can you use `infer` to recursively unwrap nested Promises? **A:** Yes. `type Unwrap<T> = T extends Promise<infer U> ? Unwrap<U> : T;` keeps unwrapping until the innermost type is not a Promise. Recursive conditional types require TypeScript 4.1 or later. **Q:** What is the difference between `infer` and mapped types? **A:** Mapped types iterate over known keys and transform them. `infer` pattern-matches against a structural shape to capture an unknown inner type. They solve completely different problems and are often used together in advanced utility types. **Q:** (Senior) Can `infer` capture types from intersection types the same way it does from unions? **A:** No. Intersections do not distribute like unions do. `T extends A & B` with `infer` placed inside `B` does not give you isolated access to what `A` contributes. You typically need separate conditional types for each part and then intersect the captured results manually. ## Examples ### Extracting array element type ```typescript type ElementType<T> = T extends readonly (infer U)[] ? U : never; type NumArr = ElementType<number[]>; // number type Mixed = ElementType<readonly [string, boolean]>; // string | boolean type NotArray = ElementType<string>; // never ``` The `readonly` in the pattern is important. Without it, passing a `readonly string[]` would fall through to `never` because the type does not match a mutable array. Adding `readonly` makes the pattern accept both mutable arrays and readonly tuples. ### Function parameter extraction ```typescript type HookParams<T> = T extends (...args: infer A) => any ? A : never; declare function useDebounce(value: string, delay: number): string; type DebounceParams = HookParams<typeof useDebounce>; // [value: string, delay: number] // Wrap any function without repeating its signature: function withLogging<T extends (...args: any[]) => any>( fn: T, ...args: HookParams<T> ): ReturnType<T> { console.log("calling with", args); return fn(...args); } ``` This pattern is particularly useful when wrapping third-party hooks or middleware. If `useDebounce` changes its signature in a library update, `HookParams` picks it up automatically without any manual type updates. ### Recursive Promise unwrapping ```typescript type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T; type A = DeepAwaited<Promise<string>>; // string type B = DeepAwaited<Promise<Promise<number>>>; // number type C = DeepAwaited<Promise<Promise<() => boolean>>>; // () => boolean type D = DeepAwaited<string>; // string (passthrough) ``` This is essentially how TypeScript's built-in `Awaited<T>` utility works under the hood. The recursive call keeps unwrapping until the innermost type is no longer a Promise. I reached for this pattern on a project where API response types were nested two or three levels deep across different service wrappers and the types kept drifting between team members once written manually.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.