Union, Intersection & Literal Types

0/6 in this phase0/21 across the roadmap

📖 Concept

Union types (A | B) represent values that can be ONE OF several types. Think of it as "either/or". Intersection types (A & B) combine multiple types into one that has ALL properties. Think of it as "both/and". Literal types narrow a type to a specific value — not just string, but exactly "hello".

Union types are everywhere in production TypeScript:

  • API response states: "loading" | "success" | "error"
  • Function parameters: string | number
  • Nullable values: string | null

Intersection types combine shapes:

  • User & Admin = an object with ALL properties from both
  • Used for mixins, extending types, and combining capabilities

Literal types give extreme precision:

  • "GET" | "POST" | "PUT" | "DELETE" — only these exact strings are allowed
  • 1 | 2 | 3 — only these exact numbers
  • Combined with unions, they create discriminated unions — one of TypeScript's most powerful patterns

Discriminated Unions: A union where each member has a common property (the "discriminant") with a unique literal type. TypeScript uses this to narrow the union in switch/if statements.

🏠 Real-world analogy: A union type is like a parking space labeled "Cars OR Motorcycles" — it accepts either. An intersection type is like a job requirement "Must have Engineering AND MBA" — need both. Literal types are like a vending machine button for a specific item — only that exact selection works.

💻 Code Example

codeTap to expand ⛶
1// Union types — either/or
2type StringOrNumber = string | number;
3function format(value: StringOrNumber): string {
4 if (typeof value === "string") {
5 return value.toUpperCase(); // TS knows it's string here
6 }
7 return value.toFixed(2); // TS knows it's number here
8}
9
10// Literal types — exact values
11type Direction = "north" | "south" | "east" | "west";
12type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
13type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
14
15function move(direction: Direction): void {
16 console.log(`Moving ${direction}`);
17}
18move("north"); // ✅
19// move("up"); // ❌ Argument of type '"up"' is not assignable
20
21// Intersection types — combine all properties
22interface HasName { name: string; }
23interface HasAge { age: number; }
24interface HasEmail { email: string; }
25
26type Person = HasName & HasAge;
27type ContactablePerson = Person & HasEmail;
28
29const person: ContactablePerson = {
30 name: "Alice",
31 age: 30,
32 email: "alice@example.com"
33};
34
35// ⭐ Discriminated Union — TypeScript's killer pattern
36type LoadingState = { status: "loading" };
37type SuccessState = { status: "success"; data: string[] };
38type ErrorState = { status: "error"; error: Error };
39
40type RequestState = LoadingState | SuccessState | ErrorState;
41
42function renderUI(state: RequestState): string {
43 switch (state.status) {
44 case "loading":
45 return "Loading...";
46 case "success":
47 return `Got ${state.data.length} items`; // TS knows data exists!
48 case "error":
49 return `Error: ${state.error.message}`; // TS knows error exists!
50 }
51}
52
53// Nullable types (with strictNullChecks)
54function findUser(id: number): User | null {
55 // might return null if not found
56 return id > 0 ? { id, name: "Alice", email: "", createdAt: new Date() } : null;
57}
58const user = findUser(1);
59// user.name; // ❌ Error: 'user' is possibly 'null'
60if (user) {
61 user.name; // ✅ After null check, TS narrows to User
62}
63
64// Template literal types (powerful with unions!)
65type Color = "red" | "blue" | "green";
66type Size = "sm" | "md" | "lg";
67type ClassName = `${Color}-${Size}`; // "red-sm" | "red-md" | ... (9 combos!)

🏋️ Practice Exercise

Mini Exercise:

  1. Create a discriminated union for shapes (Circle, Square, Triangle) and write an area calculator with exhaustive switch
  2. Create a type that represents all HTTP methods as string literals
  3. Use intersection types to combine Serializable and Validatable interfaces
  4. Create a template literal type for CSS class names like "text-sm", "text-md", "text-lg"
  5. Write a function that takes string | number | boolean and handles each case with type narrowing

⚠️ Common Mistakes

  • Confusing | (union — either) with & (intersection — both) — they are opposite operations on types, not the same as logical OR/AND on values

  • Not handling all cases in a discriminated union — use never in the default case for exhaustiveness checking

  • Thinking intersection of primitives works like union — string & number is never because no value can be both

  • Forgetting that null and undefined are separate types in strict mode — string | null and string | undefined are different

  • Using union types when a discriminated union would be safer — always prefer tagged unions for state management

💼 Interview Questions

🎤 Mock Interview

Practice a live interview for Union, Intersection & Literal Types