TypeScript
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
Exercise 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")).
Show example solution
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"));
Test yourself
-
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
- It removes the never type
Show answer
When a new member is added, the compiler reports missing cases — 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.
-
Status exhaustive switch
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 ____;
}
}
Show 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);
}
}
-
Compile-time safety parts
- 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
Next lesson: Summary - Exhaustive check