Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Combining Types  ›  Lesson

Union Types

Union Types 12 minA union type A | B accepts a value of either type, and you can only use what all its members share until you narrow it.

Union Types

A task id arrives from a form as the string "7", and from the database as the number 7. Your function should accept both. One type cannot describe that; two types joined together can.

The rule

A | B means either one; use only what both share until you narrow.

A union type lists alternatives separated by |. A value of the union may be any one of them:

function normaliseId(id: number | string): number {
  if (typeof id === "string") {
    return Number(id.trim());   // here id is a string
  }
  return id;                    // here id is a number
}

normaliseId(7);      // fine
normaliseId("7");    // fine
normaliseId(true);   // error: boolean is not in the union

The key rule is what you may do with a union value before you check it. TypeScript only allows operations valid for every member. id.toString() works, because both numbers and strings have it. id.trim() does not, because numbers have no trim.

Narrowing is how you get from "either" to "this one". A check such as typeof id === "string" tells the compiler which member you have inside that branch, and afterwards it knows the other member remains. Other checks narrow too: === undefined, Array.isArray(x), x instanceof Date, or "title" in x.

Unions of literal types are one of the most useful patterns in TypeScript:

type Status = "todo" | "doing" | "done";
let s: Status = "doing";
s = "finished";  // error

With object types, give each member a shared tag property and you get a discriminated union, which narrows cleanly on that tag:

type Result =
  | { ok: true; task: Task }
  | { ok: false; error: string };

function show(r: Result) {
  if (r.ok) { console.log(r.task.title); }
  else      { console.log(r.error); }
}

The shortcut

Treat a union like a box you have not opened. Ask "Which member do I have here?" If you do not know yet, use only what every member has. If you need something specific, open the box with a check first.

Formatting a due dateStep 1 of 4
  1. The type

    due: string | Date — some tasks store text, others a Date object.

Drills

Answer fast — trust the rule.

quick drill

Which value is NOT allowed for a variable of type number | string?

quick drill

id has type number | string. Which call compiles without any check first?

quick drill

Inside if (typeof id === "string") { ... }, what is the type of id?

quick drill

For type Result with members tagged ok: true and ok: false, how do you reach error safely?

Remember

  • A union means either one; use only what every member shares until you narrow.
  • Ask "which member do I have here?" and narrow with typeof, instanceof, in, or a tag check.
  • Trap: calling a member-specific method on an unnarrowed union.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainHandle every member

A task id can come in as a number (7) or a string ("7"). Write a function normaliseId(id: number | string): number and explain why you cannot simply call id.toFixed() or id.trim() without checking first.