Learning Guides
Menu

Conditional Types and Mapped Types

2 min readEffective TypeScript

Conditional Types and Mapped Types

These features let you compute types based on other types. They are the foundation of TypeScript’s advanced patterns.

Item 80: Use Conditional Types for Type‑Level Logic

TYPESCRIPT
type IsString<T> = T extends string ? true : false;
 
const a: IsString<"hi"> = true;
const b: IsString<123> = false;

Conditional types let you describe type‑level branching.

Item 81: Understand Distributive Conditional Types

Conditional types distribute across unions:

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

This is powerful but can be surprising; use parentheses to prevent distribution:

TYPESCRIPT
type NonDistribute<T> = [T] extends [any] ? T[] : never;
type R = NonDistribute<string | number>; // (string | number)[]

Item 82: Use infer to Extract Types

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

Item 83: Use Mapped Types for Object Transforms

TYPESCRIPT
type Optional<T> = { [K in keyof T]?: T[K] };
type ReadonlyUser = Readonly<User>;

Mapped types can add or remove modifiers:

TYPESCRIPT
type Mutable<T> = { -readonly [K in keyof T]: T[K] };

Item 84: Use Key Remapping for API Shapes

TYPESCRIPT
type Getterify<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

This is great for building fluent APIs.

Item 85: Use Template Literal Types for String Rules

TYPESCRIPT
type Route = `/${string}`;
type EventName = `on${Capitalize<string>}`;

These enforce format rules at compile time.

Scenario: Strongly Typed Event Handlers

TYPESCRIPT
type EventMap = {
  click: { x: number; y: number };
  hover: { elementId: string };
};
 
type HandlerName = `on${Capitalize<keyof EventMap & string>}`;
 
type Handlers = {
  [K in keyof EventMap as `on${Capitalize<K & string>}`]: (
    e: EventMap[K],
  ) => void;
};
 
const handlers: Handlers = {
  onClick: (e) => console.log(e.x, e.y),
  onHover: (e) => console.log(e.elementId),
};

Key Takeaways

  1. Conditional types enable type‑level branching.
  2. Distribution over unions is powerful but needs care.
  3. infer extracts type information.
  4. Mapped types transform object shapes.
  5. Template literal types enforce string formats.

Next: built‑in utility types.