Conditional Types and infer

Introduction

Conditional types pick one of two types based on a condition, similar to a ternary for types. The infer keyword extracts types inside that condition—powering utilities like ReturnType and Awaited. You will mostly use these patterns through built-ins; this chapter teaches enough to read library typings and write small helpers.

Prerequisites

Conditional Type Syntax

typescript
type IsString<T> = T extends string ? true : false;
 
type A = IsString<"hello">;
type B = IsString<number>;

Code explanation:

  • T extends string ? true : false evaluates at type level
  • A is true, B is false

Read as: “If T is assignable to string, result is true, else false.”

Distributive Behavior on Unions

When T is a naked type parameter, conditional types distribute over unions:

typescript
type ToArray<T> = T extends unknown ? T[] : never;
 
type Result = ToArray<string | number>;
// string[] | number[]

Code explanation:

  • Distribution applies to string | number separately
  • Wrap with tuple [T] to disable distribution when you need a single result

Practical: Exclude Revisited

Conceptually, Exclude works like:

typescript
type MyExclude<T, U> = T extends U ? never : T;

T extends U ? never : T removes union members assignable to U.

infer: Extract Types from Structure

typescript
type ElementType<T> = T extends (infer E)[] ? E : never;
 
type Item = ElementType<string[]>;
// string

Code explanation:

  • infer E declares a type variable matched in the extends pattern
  • For string[], E becomes string

Function return type (simplified ReturnType)

typescript
type MyReturnType<T> = T extends (...args: never[]) => infer R ? R : never;
 
function getUser() {
  return { id: 1, name: "Ada" };
}
 
type User = MyReturnType<typeof getUser>;

infer R captures whatever the function returns.

Promise unwrap (simplified Awaited)

typescript
type MyAwaited<T> = T extends Promise<infer U> ? U : T;
 
type Value = MyAwaited<Promise<number>>;
// number

Real Awaited handles nested promises; this shows the idea.

infer in Function Parameters

typescript
type FirstArg<T> = T extends (first: infer F, ...rest: never[]) => unknown ? F : never;
 
type A = FirstArg<(name: string, age: number) => void>;
// string

Useful for typing wrappers around callbacks.

Nested Conditionals

typescript
type TypeName<T> = T extends string
  ? "string"
  : T extends number
    ? "number"
    : T extends boolean
      ? "boolean"
      : "object";
 
type S = TypeName<"x">;
type N = TypeName<42>;

Prefer utility types or helper aliases when chains grow long.

Template Literal Types (Brief)

Conditionals combine with string literal types:

typescript
type EventName = "click" | "focus";
type HandlerName<T extends EventName> = `on${Capitalize<T>}`;
 
type ClickHandler = HandlerName<"click">;
// "onClick"

Common in DOM and CSS typings; advanced styling topic.

When to Write Your Own

SituationRecommendation
Need Partial / PickUse built-in utilities
Library wrapping unknown functionsConsider infer + conditional
App domain modelsPrefer interfaces and unions
Deep recursive typesProceed carefully—compiler performance

Warning

Complexity Budget

If a conditional type needs a paragraph to explain, split helpers or simplify the API shape.

Example: Unwrap Array or Single

typescript
type Flatten<T> = T extends Array<infer Item> ? Item : T;
 
type A = Flatten<string[]>;
type B = Flatten<number>;

A is string, B is number.

FAQ

Do conditional types run at runtime?

No. They exist only during type checking.

Why does my conditional type become never?

Often incompatible extends branches or distribution over empty unions. Simplify T and test each branch.

infer outside conditional types?

infer is only valid in the extends clause of conditional types.

Difference from union narrowing?

Narrowing is value-level control flow. Conditionals transform types at compile time.

Should beginners write conditional types daily?

No. Recognize them in definitions; use built-ins first.

What is T extends any?

Legacy pattern; prefer unknown in modern strict code when matching anything.