ESC

Type to search the knowledge base.

Union and Intersection Types

Combine types with | and & — model alternatives, mixins, and why unions need narrowing while intersections require all members.

beginner3 min read
  • typescript
  • union-and

Unions (A | B) mean “one of.” Intersections (A & B) mean “all of.” Most frontend modeling is unions for state and props variants; intersections show up when composing object shapes.

Docs: Union Types, Intersection Types.

Unions

type Id = string | number;
type Status = 'idle' | 'loading' | 'done';

function printId(id: Id) {
  console.log(String(id));
}

Only common operations are allowed without narrowing:

function len(x: string | string[]) {
  // return x.length; // ok — both have length
  if (typeof x === 'string') return x.toUpperCase();
  return x.join(',');
}

See narrowing.

Discriminated unions

type Result<T> =
  | { ok: true; value: T }
  | { ok: false; error: string };

Prefer tags over optional fields — discriminated unions for UI state.

Intersections

type Timestamped = { createdAt: string; updatedAt: string };
type WithId = { id: string };
type Entity = WithId & Timestamped;

const e: Entity = {
  id: '1',
  createdAt: '2026-01-01',
  updatedAt: '2026-01-02',
};

Useful for composing mixins and extending props:

type ButtonProps = { variant: 'primary' | 'secondary' };
type WithClassName = { className?: string };
type Props = ButtonProps & WithClassName;

When intersections become never

Conflicting properties:

type A = { x: string };
type B = { x: number };
type C = A & B; // x: string & number → never

That’s a design smell — fix the models.

Union of objects vs intersection

type A = { a: string };
type B = { b: number };

type U = A | B; // has a OR b (after narrowing)
type I = A & B; // must have a AND b

People mix these up in interviews — draw a Venn diagram mentally.

Narrowing checklist for unions

Technique Use for
typeof primitives
instanceof classes / DOM
equality literals
in property presence
discriminant field tagged objects
custom is guard complex shapes

Practical frontend examples

Component variants (union):

type Toast =
  | { type: 'success'; message: string }
  | { type: 'error'; message: string; retry?: () => void };

HOC / prop merge (intersection):

type Injected = { user: User };
type Own = { title: string };
type Props = Own & Injected;

Footguns

  1. Optional soup instead of unions — error?: string; data?: T.
  2. Union with any or bare string — collapses precision.
  3. Overusing intersections for variants — use unions.
  4. Huge unions — split modules; consider maps of configs.

Interview out-loud answer

“Unions are alternatives — narrow before using specific fields. Intersections combine requirements — the value must satisfy every part. I model UI state as discriminated unions and shared props as intersections or extends.”

Assignability intuition

A value of type A is assignable to A | B. A value of type A & B is assignable to A and to B. Function parameters reverse some intuitions (contravariance under strictFunctionTypes) — when callbacks fight you, draw the direction of data flow.

Further reading

Related guides