Menu

Switch case w TypeScript: zawężanie i pełne pokrycie

Instrukcja switch w TypeScript: składnia, jak każdy case zawęża typ unii, wyczerpujący switch, który nie kompiluje się, gdy brakuje case, wzorzec switch (true), fallthrough i zasięg bloku.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Instrukcja switch w TypeScript to switch z JavaScriptu ze sprawdzaniem typów. Porównuje wartość z każdym case przez ===, wykonuje pasującą gałąź i zatrzymuje się na break albo return. TypeScript zawęża sprawdzaną wartość w każdym case i potrafi sprawdzić, czy obsłużona jest każda możliwa wartość.

Etykiety ułożone jedna pod drugą (case "sat": case "sun":) dzielą jedną gałąź. return kończy funkcję, więc po nim nie trzeba break.

Składnia

switch (expression) {
    case value1:
        // runs when expression === value1
        break;
    case value2:
    case value3:
        // runs for value2 or value3
        break;
    default:
        // runs when nothing else matched
}
  • Dopasowanie używa ścisłej równości ===: case 1 nie pasuje do stringa "1".
  • Bez break, return albo throw wykonanie przechodzi do następnego case (fallthrough).
  • default jest opcjonalny i może stać w dowolnym miejscu, choć zwyczajowo stoi na końcu.
  • Wartość case, o której TypeScript potrafi udowodnić, że nigdy nie pasuje, to błąd TS2678. Dla parametru typu "a" | "b" zapis case "c": zgłasza Type '"c"' is not comparable to type '"a" | "b"'., co wyłapuje literówki w etykietach case.

Zawężanie w każdym case

W każdym case TypeScript wie, która wartość pasuje, i zawęża typ. Najbardziej przydaje się to przy discriminated union: robisz switch po wspólnej właściwości-znaczniku, a każdy case widzi pasujący wariant z jego własnymi właściwościami.

shape.radius kompiluje się tylko w case "circle". Poza nim shape może być prostokątem, który nie ma radius. Nie ma default, a funkcja i tak przechodzi sprawdzanie typów jako zwracająca number, bo TypeScript widzi, że trzy case pokrywają każdy kind. Więcej wzorców opartych na tym znajdziesz na stronie o discriminated unions.

Wyczerpujący switch z never

Powyższa funkcja przestaje się kompilować, jeśli dojdzie czwarty kształt bez własnego case (TS2366, brakujący return). Działa to jednak tylko wtedy, gdy funkcja zwraca wartość. Żeby mieć gwarancję, która działa też w kodzie void i daje czytelniejszy komunikat, dodaj default, który przypisuje wartość do never:

index.ts(14,19): error TS2322: Type '{ kind: "triangle"; base: number; height: number; }' is not assignable to type 'never'.

Po obsłużonych case jedynym typem, jaki zostaje dla shape, jest wariant trójkąta, a jego nie da się przypisać do never. Błąd nazywa dokładnie to, czego brakuje. Dodaj case "triangle": return (shape.base * shape.height) / 2;, a default zobaczy never, więc blok się skompiluje i wypisze 9. throw nadal chroni przed złymi danymi w czasie wykonania, na przykład przed kind z JSON-a, którego typy nie przewidziały.

W wielu projektach to sprawdzenie opakowuje się w funkcję pomocniczą:

Ta sama technika działa dla enumów: zrób switch po wartości enuma i w default przekaż ją do assertNever.

switch (true) dla zakresów i warunków

switch porównuje wartości, więc zakresy wymagają sztuczki: zrób switch po true i w każdym case wpisz warunek. Od TypeScript 5.3 takie warunki zawężają typy tak samo jak instrukcje if.

Case są sprawdzane po kolei, więc najbardziej szczegółowe umieść na początku. Czy to czyta się lepiej niż if / else if, to kwestia gustu, bo działanie jest takie samo.

Fallthrough i break

Case z kodem, ale bez break, return albo throw przechodzi do następnego case. Niemal zawsze jest to błąd. Opcja kompilatora noFallthroughCasesInSwitch (nie należy do strict) zamienia to w błąd TS7029, Fallthrough case in switch., a nadal pozwala na puste etykiety ułożone jedna pod drugą.

Taki celowy fallthrough działa, ale przy włączonym noFallthroughCasesInSwitch trzeba go przepisać, na przykład na osobne sprawdzenia if. Zwykle i tak jest to czytelniejsze.

Zmienne w case

Całe ciało switch to jeden blok, więc const zadeklarowany w jednym case jest widoczny w pozostałych (a ponowna deklaracja jest błędem). Otocz case nawiasami klamrowymi, żeby dostał własny zasięg:

Bez nawiasów klamrowych drugie const unit to błąd kompilacji TS2451, Cannot redeclare block-scoped variable 'unit'.

switch a obiekt z mapowaniem

Gdy każdy case tylko zamienia jedną wartość na inną, obiekt typu Record jest krótszy, a TypeScript sprawdza, czy jest w nim każdy klucz:

Pominięcie klucza to błąd kompilacji, co daje takie samo pełne pokrycie jak sprawdzenie z never. switch zostaw do przypadków, które wykonują różną logikę, zawężają warianty unii albo wcześnie kończą funkcję.

Najczęściej zadawane pytania

Jak napisać instrukcję switch w TypeScript?

Dokładnie tak jak w JavaScripcie: switch (value) { case "a": ...; break; default: ... }. Wartości case są porównywane przez ===. TypeScript dodaje sprawdzenia: wartość case, która nigdy nie pasuje do typu sprawdzanej wartości, jest błędem (TS2678), a w każdym case sprawdzana zmienna jest zawężona do tego case.

Jak sprawić, żeby switch w TypeScript był wyczerpujący?

Dodaj default, który przypisuje wartość do zmiennej typu never: default: { const unreachable: never = value; throw new Error(...) }. Gdy obsłużony jest każdy element unii, wartość ma tam typ never i kod się kompiluje. Gdy któregoś brakuje, kompilator zgłasza brakujący element (TS2322).

Jak obsłużyć kilka case tym samym kodem w switch w TypeScript?

Ułóż etykiety jedna pod drugą bez kodu pomiędzy: case "sat": case "sun": return "weekend";. Puste case przechodzą do następnego. Opcja noFallthroughCasesInSwitch zgłasza tylko case, które mają kod i nie mają break ani return, więc ułożone jedna pod drugą etykiety są nadal dozwolone.

Czy switch (true) zawęża typy w TypeScript?

Tak, od TypeScript 5.3. W switch (true) { case typeof x === "string": ... } zmienna x jest w tym case zawężona do string, tak samo jak w if. To czytelna alternatywa dla łańcucha if/else if ze sprawdzaniem zakresów albo typów.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ