#typescript #types #advanced

Day 18: Discriminated Unions

The “Tag” Pattern

Welcome to Day 18! This is the finale of Phase 3. Today we learn the “Discriminated Union” (or Tagged Union). It’s a fancy name for a simple but powerful concept.

The Problem

Imagine handling network request states.

interface NetworkState {
  state: "loading" | "success" | "failed";
  code?: number; // only for failed
  response?: { title: string }; // only for success
}

This is messy. You have to check existence of properties.

The Solution: Discriminated Unions

We create separate interfaces, each with a common discriminant (tag) property.

interface LoadingState {
  state: "loading";
}

interface FailedState {
  state: "failed";
  code: number;
}

interface SuccessState {
  state: "success";
  response: { title: string };
}

// The Union
type NetworkState = LoadingState | FailedState | SuccessState;

Now, TypeScript is incredibly smart about this.

function logger(s: NetworkState) {
  switch (s.state) {
    case "loading":
      return "Loading...";
      // s.code // Error!
    case "failed":
      return `Error ${s.code}`; // OK, knows it's FailedState
    case "success":
      return `Downloaded ${s.response.title}`; // OK
  }
}

Exhaustiveness Checking with never

We can use never to ensure we handled all cases.

function logger(s: NetworkState) {
  switch (s.state) {
    case "loading": ...
    case "failed": ...
    // Forgot "success"!
    default:
      const _exhaustiveCheck: never = s; // Error! Type 'SuccessState' is not assignable to 'never'.
      return _exhaustiveCheck;
  }
}

Phase 3 Wrap-Up

Congratulations! You are now comfortable with Advanced Types.

  • Union & Intersection.
  • Literals & Enums.
  • Generics & Constraints.
  • Type Guards & Discriminated Unions.

Coming up in Phase 4 (Day 19-23): Advanced Type Manipulation (the wizard level stuff).

Challenge for Today

  1. Create a discriminated union Shape with:
    • Circle (kind: “circle”, radius: number)
    • Square (kind: “square”, sideLength: number)
  2. Write a function getArea(s: Shape) using a switch statement regardless of the kind.
  3. Add a Rectangle later and see if your switch statement (with exhaustiveness check) warns you.

See you in Phase 4!