Sprawdzanie kompletności za pomocą never
Wykrywaj nieobsłużone przypadki w czasie kompilacji za pomocą never
Sprawdzanie kompletności za pomocą never to bezpłatna lekcja TypeScript Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej TypeScript Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs TypeScript Academy zawiera 4 lekcji w sumie.
Problem kompletności przypadków
Po dodaniu nowego elementu unii łatwo zapomnieć o jego obsłudze w którymś miejscu. Sprawdzanie kompletności przypadków zamienia takie przeoczenie w błąd kompilacji.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
// If we add "triangle" later, we want every switch to complain.Typ never
Typ never reprezentuje wartości, które nigdy nie mogą wystąpić. Jeśli obsłużono każdy przypadek, wartość docierająca do gałęzi default ma typ never.
function fail(): never {
throw new Error("unreachable");
}
// never is assignable to nothing except never itself.Przypisywanie do never w przypadku domyślnym
W gałęzi default należy przypisać wartość do zmiennej typu never. Jeśli obsłużono wszystkie warianty, przypisanie zostanie skompilowane; w przeciwnym razie pojawi się błąd.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
default:
const _exhaustive: never = s;
return _exhaustive;
}
}
console.log(area({ kind: "square", side: 3 }));Co się dzieje po pominięciu przypadku
Jeśli dodadzą Państwo element triangle, ale zapomną o jego przypadku, s w gałęzi default nie będzie już mieć typu never, więc przypisanie zakończy się błędem w czasie kompilacji.
// type Shape = ... | { kind: "triangle"; base: number; height: number };
// Now in default, s is { kind: "triangle"; ... }
// const _exhaustive: never = s; // Error: triangle not assignable to neverFunkcja pomocnicza assertNever
Wielokrotne użycie wzorca można scentralizować w funkcji assertNever. Przyjmuje ona wartość typu never i zgłasza wyjątek, dokumentując, że ta gałąź nie powinna być osiągalna.
function assertNever(value: never): never {
throw new Error("Unhandled case: " + JSON.stringify(value));
}
console.log(typeof assertNever);Używanie assertNever w instrukcji switch
W przypadku domyślnym należy wywołać assertNever(s). Wymusza to kompletność przypadków w czasie kompilacji i zapewnia czytelny błąd w czasie działania, jeśli ta gałąź zostanie osiągnięta.
function assertNever(value: never): never {
throw new Error("Unhandled: " + JSON.stringify(value));
}
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
default: return assertNever(s);
}
}
console.log(area({ kind: "circle", radius: 1 }).toFixed(2));Bezpieczeństwo w czasie kompilacji a bezpieczeństwo w czasie działania
Sprawdzenie za pomocą never wykrywa brakujące przypadki przed uruchomieniem kodu, a zgłoszony wyjątek chroni program, jeśli coś przejdzie przez sprawdzanie typów i wystąpi w czasie działania.
function assertNever(x: never): never {
throw new Error("Unhandled: " + String(x));
}
// Compile error if a case is missing; runtime throw as a backstop.
console.log("two layers of safety");Kompletność przypadków bez default
Jeśli funkcja ma jawnie określony typ zwracany, a instrukcja switch obejmuje każdy przypadek, TypeScript może również wskazać brak instrukcji return — to kolejna forma sprawdzania kompletności przypadków.
type Light = "red" | "yellow" | "green";
function next(l: Light): Light {
switch (l) {
case "red": return "green";
case "yellow": return "red";
case "green": return "yellow";
}
// No default needed; all cases return.
}
console.log(next("red"));Kompletność przypadków w łańcuchach if/else
Ta sama idea działa z konstrukcją if/else. Po obsłużeniu każdego wariantu końcowa gałąź else otrzymuje wartość typu never.
function assertNever(x: never): never { throw new Error("bad"); }
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function name(s: Shape): string {
if (s.kind === "circle") return "circle";
else if (s.kind === "square") return "square";
else return assertNever(s);
}
console.log(name({ kind: "square", side: 2 }));Dlaczego never jest właściwym narzędziem
Ponieważ typu never nie można przypisać do żadnego innego typu, każdy pozostały wariant spowoduje błąd przypisania. Dzięki temu never doskonale wykrywa nieobsłużone przypadki.
// Only never is assignable to never.
let x: never;
// x = "hi"; // Error
// Any concrete leftover type fails the same way.
console.log("never catches gaps");Kompletność przypadków jako zabezpieczenie podczas refaktoryzacji
Jeśli używają Państwo assertNever we wszystkich odpowiednich miejscach, dodanie elementu unii wygeneruje uporządkowaną listę błędów kompilacji wskazujących dokładnie każde miejsce wymagające aktualizacji.
function assertNever(x: never): never { throw new Error("unhandled"); }
type Status = "idle" | "busy";
function render(s: Status): string {
switch (s) {
case "idle": return "Idle";
case "busy": return "Busy";
default: return assertNever(s);
}
}
console.log(render("idle"));Szybkie sprawdzenie: kompletność przypadków
Sprawdź, czy rozumiesz sprawdzanie kompletności przypadków.
Podsumowanie: kompletność przypadków z never
Nauczyli się Państwo przypisywać wartość domyślną do never lub przekazywać ją do assertNever, aby kompilator wymuszał obsługę każdego wariantu. Dzięki temu zapomniane przypadki stają się błędami kompilacji.
function assertNever(x: never): never { throw new Error("unhandled"); }
type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };
function f(s: Shape) {
switch (s.kind) {
case "circle": return s.radius;
case "square": return s.side;
default: return assertNever(s);
}
}
console.log(f({ kind: "circle", radius: 5 }));Często zadawane pytania
Czy lekcja „Sprawdzanie kompletności za pomocą never” jest bezpłatna?
Tak — pełny tekst „Sprawdzanie kompletności za pomocą never” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu TypeScript Academy, przejdź na CoddyKit PRO. Kurs TypeScript Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Sprawdzanie kompletności za pomocą never”?
Wykrywaj nieobsłużone przypadki w czasie kompilacji za pomocą never Ćwiczysz TypeScript Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć TypeScript Academy?
Nie wymagamy żadnego doświadczenia. TypeScript Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Sprawdzanie kompletności za pomocą never”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji TypeScript Academy?
Tak. Każda lekcja TypeScript Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Budowanie unii rozróżnianych
- Zawężanie na podstawie właściwości rozróżniającej
- Sprawdzanie kompletności za pomocą never
- Modelowanie automatów stanów