Technology · TypeScript

TypeScript Advanced Types

Learn conditional types, mapped types, template literals, and advanced pattern matching.

TL;DR
  1. 01Use conditional types for type-level if-else logic.
  2. 02Use mapped types to transform keys and values in existing types.
  3. 03Use template literal types for string manipulation at the type level.

Conditional Types

  • Use conditional types with the ternary syntax for type-level decisions.
    type IsString<T> = T extends string ? true : false;
    
    type A = IsString<"hello">;  // true
    type B = IsString<number>;   // false
  • Check constraints and narrow types based on conditions.
    type Flatten<T> = T extends Array<infer U> ? U : T;
    
    type Str = Flatten<string[]>;  // string
    type Num = Flatten<number>;    // number
  • Use the infer keyword to extract types from generic patterns.
    type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
    
    type NumReturn = ReturnType<() => number>;  // number
  • Distribute conditional types over union types automatically.
    type ToArray<T> = T extends any ? T[] : never;
    
    type Result = ToArray<string | number>;  // string[] | number[]

Mapped Types

  • Transform every key in an object type to a new shape.
    type Readonly<T> = {
      readonly [K in keyof T]: T[K];
    };
    
    type User = { name: string; age: number };
    type ReadonlyUser = Readonly<User>;
    // { readonly name: string; readonly age: number }
  • Make all properties optional using mapped types.
    type Optional<T> = {
      [K in keyof T]?: T[K];
    };
  • Extract values and create a mapped type from them.
    type Getters<T> = {
      [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
    };
  • Use as clauses to rename keys during mapping.
    type Getters<T> = {
      [K in keyof T as `get${Capitalize<K & string>}`]: () => T[K];
    };
    
    type UserGetters = Getters<User>;
    // { getName: () => string; getAge: () => number }

Template Literal Types

  • Build string types using template literals.
    type Event<T extends string> = `on${Capitalize<T>}`;
    
    type ClickEvent = Event<"click">;  // "onClick"
    type ChangeEvent = Event<"change">; // "onChange"
  • Combine and manipulate strings at the type level.
    type Path = "user" | "post" | "comment";
    type GetRoute = `GET /${Path}`;
    // "GET /user" | "GET /post" | "GET /comment"
  • Extract parts of string types with conditional types.
    type Split<T extends string, D extends string> =
      T extends `${infer F}${D}${infer R}`
        ? [F, ...Split<R, D>]
        : [T];
    
    type Parts = Split<"a-b-c", "-">;  // ["a", "b", "c"]

Advanced Utility Patterns

  • Build sophisticated utility types combining multiple features.
    type DeepPartial<T> = T extends object
      ? { [K in keyof T]?: DeepPartial<T[K]> }
      : T;
  • Create recursive type transformations.
    type DeepReadonly<T> = T extends object
      ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
      : T;
  • Compose multiple conditional types for complex logic.
    type Flatten<T> = T extends Array<infer U>
      ? Flatten<U>
      : T extends object
      ? U extends object
        ? Flatten<U>
        : T
      : T;
  • Use type predicates to narrow based on relationships.
    type IsAssignableTo<T, U> = T extends U ? true : false;

Practical Applications

  • Build type-safe API response handlers with conditional types.
    type ApiResponse<T extends "success" | "error"> =
      T extends "success"
        ? { status: 200; data: unknown }
        : { status: 400; error: string };
  • Create database query builders with mapped and conditional types.
    type SelectBuilder<T> = {
      [K in keyof T as `select${Capitalize<K & string>}`]: () => void;
    };
  • Build framework-like type systems with advanced features.
    type Validator<T> = {
      [K in keyof T]: (v: T[K]) => boolean;
    };

Tip: Use conditional types with infer to extract and reuse parts of complex types in your type transformations.

Warning: Advanced types are powerful but can become hard to read — document complex type transformations and keep them focused on single concerns.

TypeScript Utility TypesTypeScript Async Patterns