Compile-time safety
- TypeScript course
- Advanced
- Lesson 35/57
- 4 min
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 calledPractice 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
// 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ırSample 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"));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
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 ____;
}
}
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 |