Back to Blog
Lesson 42 of the TypeScript: Typing JavaScript with Confidence course
TypeScriptAugust 29, 20263 min read

Inferring Types in Conditionals: Mastering the Infer Keyword

Learn to use the 'infer' keyword in TypeScript to extract types from functions and promises. Build advanced generic utilities for cleaner, type-safe code.

TypeScriptGenericsAdvanced TypesType InferenceConditional Types
Close-up of a vintage typewriter with text 'turn the page' typed on paper, symbolizing change and new beginnings.

Previously in this course, we explored Conditional Types in TypeScript: Dynamic Logic for Robust Apps. While those allowed us to branch logic based on types, we were limited to checking if a type matched a pattern. Today, we add the infer keyword, which lets us capture and reuse pieces of that type pattern dynamically.

What is 'infer'?

Think of infer as a variable declaration for types inside a conditional type. When you use the extends clause in a conditional, you are matching a structure. The infer keyword allows you to "label" a sub-part of that structure so you can reference it in the resulting type.

Without infer, you can only return a static type. With infer, you can say: "If this matches a function, please extract the return type and give it to me."

Extracting Return Types

A common real-world requirement is extracting the return type of a function without having to manually define it again. Let's see how we can build a GetReturnType utility.

TYPESCRIPT
type GetReturnType<T> = T extends (...args: any[]) => infer R 
  ? R 
  : never;

// Usage:
function getTaskTitle(id: number): string {
  return "Complete TS Course";
}

type TaskTitle = GetReturnType<typeof getTaskTitle>; 
// TaskTitle is now CE9178">'string'

In this example, the infer R tells TypeScript: "Whatever the return type of this function is, assign it to the placeholder R." If the input T is indeed a function, we return R. Otherwise, we return never.

Building Advanced Generic Utilities

We can take this further by extracting types from complex wrappers, such as Promise types. This is essential for our ongoing task API client, where we often deal with async responses.

TYPESCRIPT
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// Usage in our API client:
async function fetchTask(id: number) {
  return { id: 1, title: "Learn Infer" };
}

type TaskResponse = UnwrapPromise<ReturnType<typeof fetchTask>>;
// TaskResponse is { id: number, title: string }

By combining ReturnType (a built-in helper that uses infer under the hood) and our custom UnwrapPromise, we can cleanly extract the underlying data structure from our asynchronous API calls.

Hands-on Exercise

In your current task API client project, you likely have a function that returns a Promise<Task[]>. Create a utility type called ExtractTaskArray that accepts this promise-returning function type and returns the Task[] type directly.

  1. Create a function getTasks(): Promise<Task[]>.
  2. Write a type ExtractTaskArray<T> using infer.
  3. Use it to define type TaskList = ExtractTaskArray<typeof getTasks>.

Common Pitfalls

  • Inferring in the wrong place: You can only use infer within the extends clause of a conditional type. It cannot be used in standard type aliases or interfaces.
  • Too many inferences: If you try to infer multiple parts of a type, keep your patterns simple. Nested inference can quickly become difficult to read and debug.
  • Ignoring the 'false' branch: Always provide a fallback (like never or T itself) in your ternary operator. If your inference fails, an unhandled conditional type will result in unknown or any, which defeats the purpose of your type-safe utility.

FAQ

Can I infer multiple types at once? Yes. You can use multiple infer keywords in a single pattern, such as T extends (a: infer A, b: infer B) => void.

Is infer supported in all TypeScript versions? infer was introduced in TypeScript 2.8. As long as you are using a modern version (which you should be, per our setup in Setting Up the TypeScript Environment), it is fully supported.

How does this differ from Generics? Generics receive types, while infer extracts types from existing structures. They are two sides of the same coin when building flexible, reusable code.

Recap

We've moved from simple conditional branching to sophisticated type extraction. By using the infer keyword, you can now write generic utilities that dynamically inspect and pull apart complex types—reducing the need for manual type duplication and keeping your API client layer perfectly in sync with your data.

Up next: We will perform the Final Refactoring of the Task Client, applying these advanced techniques to make our API layer truly robust and DRY.

Similar Posts