TypeScript Unions

Unions, Enums & Discriminated Unions

Union types model "one of several shapes", the pattern at the heart of idiomatic TypeScript. Discriminated unions make them safe to work with.

TypeScript 5 Interview Key 4 Topics
01

Union & Intersection Types

A union (A | B) is a value that is either A or B. An intersection (A & B) is a value that is A and B at once (has all properties of both).

TypeScript — Union & Intersection
// union — one of several types
type ID = string | number;
let id: ID = 42;
id = "a1";                 // both allowed

// on a union you can only use members COMMON to all types...
function len(x: string | string[]) {
  return x.length;       // OK — both have .length
  // x.toUpperCase();     // Error — arrays don't have it, must narrow first
}

// intersection — combine into one shape with all props
type Named = { name: string };
type Aged  = { age: number };
type Person = Named & Aged;   // { name: string; age: number }
02

Literal Types

A literal type is a type whose only value is one specific string, number, or boolean. Unions of literals give you type-safe "enum-like" sets without a runtime enum.

TypeScript — Literal Unions
// a set of allowed string values, checked at compile time
type Direction = "north" | "south" | "east" | "west";

function move(dir: Direction) { /* ... */ }
move("north");          // OK
// move("up");           // Error: not assignable to Direction

// combine with numbers
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
Literal-union types are the lightweight, idiomatic alternative to enums for a fixed set of string options. They vanish at compile time (no runtime object) and autocomplete beautifully.
03

Discriminated Unions

A discriminated union gives each member a shared literal "tag" field. Switching on that tag lets TypeScript narrow to the exact member, so each branch knows precisely which properties exist. This is the single most powerful TS modelling pattern.

TypeScript — Discriminated Union + Exhaustiveness
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number }
  | { kind: "rect"; w: number; h: number };

function area(s: Shape): number {
  switch (s.kind) {
    case "circle": return Math.PI * s.radius ** 2;   // s.radius known here
    case "square": return s.side ** 2;
    case "rect":   return s.w * s.h;
    default:
      const _exhaustive: never = s;   // compile error if a case is missed
      return _exhaustive;
  }
}
The never assignment in default is an exhaustiveness check: if you later add a new shape and forget to handle it, TypeScript errors here at compile time. This is how TS turns "did I cover every case?" into a guarantee.
04

Enums

An enum is a named set of constants that does exist at runtime (unlike literal unions). Useful when you need the values reflected at runtime, but often a literal union or as const object is preferred.

TypeScript — enum
enum Status { Active, Inactive, Pending }   // 0, 1, 2
let s: Status = Status.Active;

// string enums — more readable at runtime
enum Level { Low = "LOW", High = "HIGH" }

// modern alternative: 'as const' object + literal union
const Roles = { Admin: "admin", User: "user" } as const;
type Role = typeof Roles[keyof typeof Roles];   // "admin" | "user"
Numeric enums generate reverse mappings and can behave surprisingly. Many TS style guides prefer string literal unions or as const objects over enums, which produce a runtime object you might not want.

Quick Quiz

1. On a value of type string | number, you can directly use…
2. A & B (intersection) produces a type with…
3. A discriminated union narrows via…
4. Assigning to a never in a switch default gives you…
5. Unlike a literal union, an enum…