CodeBlanks Logo Icon CodeBlanks

Compile-time safety

Learn to guarantee compile-time safety.

Compile-time safety

Exhaustive check provides compile-time safety. When a new union member is added, the cases in all switches are no longer exhaustive; the assertNever call gives a compile error and reports the missing places. This prevents runtime errors.

type Status = "pending" | "paid" | "shipped";

function label(s: Status): string {
  switch (s) {
    case "pending": return "Pending";
    case "paid": return "Paid";
    case "shipped": return "Shipped";
    default: return assertNever(s);
  }
}

Runtime vs compile time

The assertNever function throws at runtime; its real value is at compile time. If all cases are written, the default branch is never reached and the throw never runs. The real protection is the compiler catching the missing case.

function assertNever(x: never): never {
  throw new Error("Unexpected: " + x);
}

// If all cases are written, default is never reached
// assertNever is never called

Practice Status label with exhaustive check

Define an assertNever(x: never): never function (with throw new Error("Unexpected") inside). Define the Status = "pending" | "paid" | "shipped" union type. Write a label(s: Status): string function using switch (s): for pending return "Beklemede", for paid return "Ödendi", for shipped return "Gönderildi", for default write return assertNever(s);. Print the result via console.log(label("shipped")).

Starter code

index.ts
// assertNever fonksiyonunu tanımla (never parametre, never dönüş, throw içersin)

// Status union türünü tanımla (pending, paid, shipped)

// label fonksiyonunu switch ile yaz, default'ta assertNever çağır

// "shipped" durumunun etiketini yazdır

Sample solution

index.ts
function assertNever(x: never): never {
  throw new Error("Unexpected");
}

type Status = "pending" | "paid" | "shipped";

function label(s: Status): string {
  switch (s) {
    case "pending":
      return "Beklemede";
    case "paid":
      return "Ödendi";
    case "shipped":
      return "Gönderildi";
    default:
      return assertNever(s);
  }
}

console.log(label("shipped"));

Expected output

Gönderildi

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 How does exhaustive check provide compile-time safety?

  • It throws at runtime
  • It auto-writes all cases
  • When a new member is added, the compiler reports missing cases Correct answer
  • It removes the never type

Explanation: When a new member is added, in switches with missing cases the variable in the default branch is not never; the assertNever call gives a compile error and reports the missing places.

2 Status exhaustive switch

Question
function assertNever(x: never): never {
  throw new Error("Unexpected");
}

type Status = "pending" | "paid" | "shipped";

function label(s: Status): string {
  switch (s) {
    case "pending": return "Beklemede";
    case "paid": return "Ödendi";
    case "shipped": return "Gönderildi";
    default:
      return ____;
  }
}
Answer
function assertNever(x: never): never {
  throw new Error("Unexpected");
}

type Status = "pending" | "paid" | "shipped";

function label(s: Status): string {
  switch (s) {
    case "pending": return "Beklemede";
    case "paid": return "Ödendi";
    case "shipped": return "Gönderildi";
    default:
      return assertNever(s);
  }
}

Explanation: Calling assertNever(s) in the default branch gives a compile error if s is not never (i.e. a case is missing); this provides compile-time safety.

3 Compile-time safety parts

Term Meaning
assertNever(x: never) parameter must be never
Tüm case'ler default not reached
Yeni üye eklenince missing case errors at compile
Derleme zamanı real safety here

All lessons in Advanced