TypeScript switch/case - Pisz bezpieczny i czysty kod!

Kod TypeScript z instrukcją `switch case` dla enum `Animals`. Funkcja `getSound` zwraca dźwięk zwierzęcia.

Napisano przez

Tymoteusz Sobczak

Opublikowano

11 cze 2026

Spis treści

W TypeScripcie `switch` ma sens wtedy, gdy wybierasz jedną z kilku znanych opcji i chcesz, żeby kod był czytelniejszy niż długi łańcuch `if...else`. Dobrze użyta konstrukcja potrafi uprościć logikę, ale źle użyta szybko wprowadza błędy z `break`, przypadkowym przejściem do kolejnego `case` i niedomkniętymi wariantami typu. Poniżej pokazuję, jak pisać taki kod praktycznie, bez sztucznej teorii i bez pułapek, które wciąż pojawiają się nawet w dojrzałych projektach.

Najkrótsza droga do poprawnego użycia switcha w TypeScripcie

  • `switch` najlepiej sprawdza się przy porównywaniu jednej wartości z kilkoma stałymi wariantami.
  • Dopasowanie w JavaScripcie działa przez porównanie ścisłe, więc nie licz na automatyczne rzutowanie typów.
  • `break` chroni przed przejściem do kolejnego `case`, a `return` lub `throw` także kończą wykonanie bloku.
  • W TypeScripcie bardzo dobrze działa wzorzec z unią typów i kontrolą wyczerpującą przez `never`.
  • Przy `let` i `const` w `case` warto stosować dodatkowe klamry, bo same etykiety `case` nie tworzą osobnego zasięgu.
  • Jeśli warunki nie są prostym dopasowaniem wartości, często lepszy będzie `if...else` albo mapa obiektów.

Jak działa switch i kiedy ma przewagę nad if else

Podstawowa idea jest prosta: bierzesz jedną wartość, sprawdzasz ją kolejno przeciwko kilku `case` i wykonujesz pasujący blok. MDN opisuje to wprost: dopasowanie odbywa się przez porównanie ścisłe, więc `1` nie jest tym samym co `"1"`, a `true` nie zadziała jak dowolny „prawdziwy” warunek. To ważne, bo wiele osób traktuje `switch` jak wygodniejszą wersję `if...else`, a to nie do końca to samo.

Ja najczęściej sięgam po `switch`, gdy mam jeden stabilny punkt decyzji: status zamówienia, typ akcji, nazwę widoku, rodzaj komunikatu albo rozgałęzienie po unii typów. Jeśli warunki są zależne od zakresów, złożonych porównań albo kilku różnych zmiennych, `if...else` zwykle pozostaje czytelniejszy. `switch` wygrywa tam, gdzie logika jest płaska i opiera się na jawnych wariantach, a nie na obliczeniach.

Przykład praktyczny jest prosty: jeśli obsługujesz statusy `pending`, `paid`, `shipped` i `cancelled`, `switch` pokazuje od razu pełną listę możliwych dróg. W takim układzie kod czyta się szybciej niż serię porównań rozrzuconych po kilku liniach. To prowadzi do składni, która wygląda banalnie, ale ma kilka detali wartych dopracowania.

Składnia, która oszczędza błędów

W samym TypeScripcie składnia jest taka sama jak w JavaScripcie, bo kompilator nie wymyśla własnego `switch`. Różnica zaczyna się dopiero na etapie typów i kontroli błędów. W praktyce najbezpieczniej jest pisać konstrukcję w prostym, przewidywalnym układzie:

type OrderStatus = "pending" | "paid" | "shipped" | "cancelled";

function getStatusLabel(status: OrderStatus): string {
  switch (status) {
    case "pending":
      return "Oczekuje na płatność";
    case "paid":
      return "Opłacone";
    case "shipped":
      return "Wysłane";
    case "cancelled":
      return "Anulowane";
    default:
      return "Nieznany status";
  }
}

Najważniejsze są tu trzy rzeczy. Po pierwsze, każdy `case` kończy się `return`, więc nie ma ryzyka przypadkowego przejścia dalej. Po drugie, `default` istnieje nawet wtedy, gdy formalnie nie jest konieczny, bo ułatwia czytanie i daje bezpieczny punkt awaryjny. Po trzecie, w TypeScripcie taki kod dobrze współpracuje z literalnymi typami, czyli wartościami w stylu dokładnie `"paid"` zamiast ogólnego `string`.

Warto też pamiętać o zasięgu. Same etykiety `case` nie tworzą osobnego zakresu dla zmiennych, więc jeśli wewnątrz kilku gałęzi deklarujesz `const` albo `let`, zamknij daną gałąź w bloku. W przeciwnym razie można dostać błąd o ponownej deklaracji identyfikatora, mimo że na pierwszy rzut oka każdy `case` wygląda jak osobny fragment kodu. To drobiazg, ale właśnie takie drobiazgi najczęściej psują dobrze rozpoczęty refaktor.

Jeśli chcesz uniknąć podobnych wpadek, następna sekcja pokazuje, jak pisać `case` tak, żeby kompilator pomagał, a nie tylko „przepuszczał” kod dalej.

Jak wykorzystać switch z unią typów i kontrolą wyczerpującą

Tu TypeScript daje realną przewagę nad czystym JavaScriptem. Dokumentacja TypeScript pokazuje wzorzec z `never`, który pozwala wymusić pełną obsługę wszystkich wariantów. To szczególnie przydatne, gdy pracujesz na unii typów i chcesz mieć pewność, że po dodaniu nowego wariantu kompilator od razu wskaże brakującą gałąź.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; sideLength: number }
  | { kind: "triangle"; sideLength: number };

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    case "triangle":
      return (shape.sideLength * shape.sideLength) / 2;
    default: {
      const exhaustiveCheck: never = shape;
      return exhaustiveCheck;
    }
  }
}

Ten wzorzec ma dużą wartość praktyczną. Jeśli później dodasz np. `rectangle`, a zapomnisz o nim w `switch`, TypeScript zgłosi błąd w miejscu, w którym naprawdę trzeba poprawić logikę. To lepsze niż ciche „przepuszczenie” nowego przypadku do `default`, bo wtedy brak obsługi wychodzi dopiero w produkcji.

Ja traktuję to jako jedną z sensowniejszych rzeczy, które TypeScript wnosi do zwykłego `switch`: nie tylko porządkuje kod, ale też pilnuje kompletności decyzji. Z tego miejsca łatwo już przejść do najczęstszych błędów, bo właśnie przy takich konstrukcjach pojawiają się najczęściej.

Kod TypeScript z instrukcją `switch case` dla enum `Animals`. Funkcja `getSound` zwraca dźwięk zwierzęcia.

Jak pisać bezpieczne case’y i nie wpaść w klasyczne pułapki

  • Nie zapominaj o `break` tam, gdzie ma nastąpić zakończenie gałęzi. Bez niego kod przejdzie do następnego `case`, a to bywa trudne do zauważenia w dużych blokach.
  • Włącz `noFallthroughCasesInSwitch`. To ustawienie w `tsconfig` raportuje przypadki, w których niechciany fall-through mógłby przejść niezauważony.
  • Nie zakładaj, że `case` tworzy własny zakres. Jeśli używasz `const` lub `let`, dodaj klamry wokół zawartości gałęzi.
  • Nie mieszaj zbyt wielu odpowiedzialności w jednym `switch`. Gdy blok robi trzy różne rzeczy, zwykle jest już za ciężki.
  • Uważaj na przypadkowe porównania typów. `switch` nie zrobi za Ciebie konwersji wartości, więc liczby i napisy trzeba trzymać konsekwentnie.
  • Wykorzystuj celowy fall-through tylko wtedy, gdy naprawdę upraszcza kod. Dwa lub trzy `case` wykonujące ten sam fragment to dobry przykład, ale złożona sekwencja działań bywa już zbyt mało czytelna.

W praktyce większość problemów sprowadza się do jednego: ktoś pisze `switch` tak, jakby był z natury prosty i „sam się obroni”. Nie obroni się. Pomaga tu tylko konsekwencja, jasne reguły i krótki blok odpowiedzialności dla jednej decyzji. To prowadzi naturalnie do pytania, kiedy w ogóle nie warto używać `switch`.

Kiedy switch przegrywa z if else albo mapą obiektów

Nie każdy przypadek zasługuje na `switch`. Jeśli warunek zależy od zakresu liczb, daty, kilku pól naraz albo wyrażeń logicznych typu „jeśli A i B, ale nie C”, `if...else` będzie po prostu lepszy. Z kolei przy bardzo prostym mapowaniu klucz-wynik często wygrywa obiekt lub `Map`, bo usuwa zbędny ceremonialny kod.

Sytuacja Lepszy wybór Dlaczego
Jedna wartość, kilka stałych wariantów `switch` Najczytelniej pokazuje zamkniętą listę opcji.
Warunki zakresowe i złożone porównania `if...else` Łatwiej opisać logikę niż sztucznie rozbijać ją na `case`.
Proste mapowanie klucza na wynik Obiekt lub `Map` Mniej kodu, mniej szans na pomyłkę, łatwiejsza rozbudowa danych.
Obsługa zamkniętej unii typów `switch` z `never` Kompletność obsługi może być sprawdzana przez kompilator.
Warunki zależne od wielu zmiennych `if...else` albo funkcje pomocnicze `switch` szybko traci wtedy przejrzystość.

Jest jeszcze wzorzec `switch (true)`, czasem spotykany w starszym kodzie. Technicznie działa, ale w nowych projektach traktuję go ostrożnie, bo maskuje prostą logikę porównań pod konstrukcją, która wygląda na bardziej formalną, niż jest w rzeczywistości. Jeśli musisz dopisywać komentarz, żeby wytłumaczyć, czemu `switch` sprawdza warunki logiczne, to zwykle sygnał, że lepiej wrócić do `if...else`. Z tego miejsca zostaje już tylko jedna rzecz: sensowny wzorzec pracy, który możesz wdrożyć od razu.

Wzorzec, który najlepiej sprawdza się w codziennym kodzie

Jeśli miałbym zostawić jeden praktyczny schemat, to wyglądałby tak: wybierz `switch` tylko wtedy, gdy porównujesz jedną wartość z zamkniętym zestawem wariantów; trzymaj każdy `case` krótko; kończ go `return`, `break` albo `throw`; a przy TypeScripcie dodaj kontrolę wyczerpującą przez `never`, jeśli pracujesz na unii typów. To daje kod, który jest jednocześnie czytelny, bezpieczny i odporny na późniejsze dopisywanie nowych przypadków.

W codziennej pracy najbardziej doceniam nie samą składnię, tylko to, że dobrze napisany `switch` ogranicza chaos w logice decyzyjnej. Widzisz od razu, jakie są obsługiwane warianty, gdzie kończy się gałąź i czy kompilator zauważy brak nowego przypadku. Jeśli trzymasz się tych zasad, `switch/case` w TypeScripcie przestaje być tylko prostą konstrukcją składniową, a zaczyna być naprawdę solidnym narzędziem do porządkowania decyzji w kodzie.

FAQ - Najczęstsze pytania

`Switch` jest lepszy, gdy porównujesz jedną wartość z kilkoma stałymi wariantami (np. statusy, typy akcji). Zapewnia wtedy lepszą czytelność i strukturę kodu niż długi łańcuch `if...else`, szczególnie przy obsłudze unii typów.

Aby uniknąć przypadkowego przejścia do kolejnego `case` (fall-through), zawsze kończ każdą gałąź `case` za pomocą `break`, `return` lub `throw`. Włącz też opcję `noFallthroughCasesInSwitch` w `tsconfig`, aby kompilator raportował takie przypadki.

To wzorzec, który wykorzystuje typ `never` w bloku `default` instrukcji `switch` do wymuszenia obsługi wszystkich wariantów unii typów. Jeśli dodasz nowy wariant i zapomnisz go obsłużyć, TypeScript zgłosi błąd kompilacji, zwiększając bezpieczeństwo kodu.

Nie, same etykiety `case` nie tworzą osobnego zakresu. Jeśli deklarujesz zmienne `const` lub `let` wewnątrz `case`, umieść zawartość gałęzi w dodatkowych klamrach `{}`. Zapobiega to błędom ponownej deklaracji identyfikatora.

Przy prostym mapowaniu klucza na wynik, obiekt literalny lub `Map` często jest bardziej zwięzły i czytelny niż `switch`. Eliminuje to zbędny "ceremonialny" kod i ułatwia zarządzanie danymi, szczególnie gdy mapowanie jest dynamiczne.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi:

typescript switch case typescript switch case pułapki switch case typescript unie typów switch case typescript a if else typescript switch case never

Udostępnij artykuł

Tymoteusz Sobczak

Tymoteusz Sobczak

Nazywam się Tymoteusz Sobczak i mam 9-letnie doświadczenie w programowaniu webowym. Moja przygoda z tą dziedziną zaczęła się od fascynacji tworzeniem stron internetowych, co z czasem przerodziło się w pasję do dzielenia się wiedzą i pomagania innym w odkrywaniu tajników programowania. Lubię wyjaśniać złożone zagadnienia w przystępny sposób, co pozwala moim czytelnikom lepiej zrozumieć temat i rozwijać swoje umiejętności. Pisząc dla jscwiczenia.pl, koncentruję się na dostarczaniu aktualnych i rzetelnych informacji, które są zrozumiałe nawet dla osób dopiero zaczynających swoją przygodę z programowaniem. Staram się porównywać różne źródła, śledzić najnowsze trendy i organizować wiedzę w sposób, który ułatwia naukę. Moim celem jest, aby każdy mógł znaleźć tu przydatne materiały, które pomogą mu w budowaniu kariery w programowaniu webowym.

Napisz komentarz