Advanced TypeScript for Harness Builders

2. Discriminated Unions

A common literal property lets TypeScript narrow a union inside switch/if blocks.

A discriminated union uses a common literal property (the “discriminant”) to let TypeScript narrow the type inside switch/if blocks.

File: packages/ai/src/types.ts L247-259

export type AssistantMessageEvent =
  | { type: "start"; partial: AssistantMessage }
  | { type: "text_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
  | { type: "done"; reason: Extract<StopReason, "stop" | "length" | "toolUse">; message: AssistantMessage }
  | { type: "error"; reason: Extract<StopReason, "aborted" | "error">; error: AssistantMessage };

The type field is the discriminant. Each branch carries different payload shapes.

Another large example — RPC commands with 20+ variants:

File: packages/coding-agent/src/modes/rpc/rpc-types.ts L19-68

export type RpcCommand =
  | { id?: string; type: "prompt"; message: string; images?: ImageContent[] }
  | { id?: string; type: "abort" }
  | { id?: string; type: "set_model"; provider: string; modelId: string }
  | { id?: string; type: "bash"; command: string };
// ... 20+ more variants

Open this chapter inside the full course