programowanieFrontend developer

Jak działa mechanizm typizacji union types (typów złożonych) w TypeScript? Po co jest potrzebny, jak działa zawężanie typu i jakie są niuanse pracy z typami union?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania:

Union types pojawiły się w TypeScript w celu opisu zmiennych i parametrów, które mogą przyjmować wartości różnych typów. Ta możliwość znacznie rozszerzyła elastyczność typizacji w porównaniu z klasycznymi językami.

Problem:

W JavaScript funkcje i zmienne często mają wiele formatów (na przykład przyjmują liczbę lub tekst), co komplikuje bezpieczną typizację. Bez union types trzeba było używać typu any lub dublować kod. To zwiększało liczbę błędów w produkcie i utrudniało pracę w dużych zespołach.

Rozwiązanie:

Union types pozwalają zadeklarować zmienną, która może być jednym z kilku typów, gwarantując poprawne operacje po sprawdzeniu. Zrealizowano również wsparcie dla zawężania typów (type narrowing), co pomaga kompilatorowi „zrozumieć”, z czym ma do czynienia.

Przykład kodu:

function formatId(id: number | string): string { if (typeof id === 'string') { return id.toUpperCase(); } return id.toString(); }

Kluczowe cechy:

  • Pozwalają na wyraźne wskazanie możliwych typów zmiennych i parametrów.
  • Działają razem z mechanizmem zawężania typów przez sprawdzenia (na przykład, typeof lub in).
  • Utrudniają pracę z obiektami, gdzie ten sam klucz może mieć różne typy — wymaga starannego zarządzania logiką dostępu.

Pytania z podstępem.

Czy można zapisać union z obiektami o różnych właściwościach i odwoływać się do dowolnej właściwości bez sprawdzenia?

Nie. Union types pozwalają odwoływać się tylko do tych właściwości i metod, które są obecne we wszystkich typach. Aby uzyskać dostęp do prywatnych właściwości, wymagana jest zawężenie typu.

Przykład kodu:

type Fish = { swim: () => void; }; type Bird = { fly: () => void; }; function move(animal: Fish | Bird) { // animal.swim(); // Błąd bez zawężenia if ('swim' in animal) { animal.swim(); // OK } }

Dlaczego nie wszystkie metody są dostępne dla union typu string | number?

TypeScript w union pozwala tylko na to, co jest we wszystkich uwzględnionych typach. Aby używać indywidualnych metod, należy najpierw sprawdzić rzeczywisty typ.

Co się stanie, jeśli nie sprawdzasz typu w union, a spróbujesz wywołać specyficzną metodę?

W takim przypadku wystąpi błąd kompilacji, ponieważ nie gwarantuje się obecności metody. Działa to tylko po sprawdzeniu konkretnego typu.

Typowe błędy i anty-wzorce

  • Pominięcie sprawdzenia typu przed użyciem (może wywołać błąd dostępu).
  • Opis zbyt szerokich typów union (traci się bezpieczeństwo typowe).
  • Błędne zawężenie prowadzi do niewidocznych błędów i not exhaustive checks.

Przykład z życia

Negatywny przypadek

Zmienna otrzymała typ string | number i bez sprawdzenia wywołano toUpperCase(). W rezultacie aplikacja się zawiesza przy danych numerycznych.

Zalety:

  • Szybko napisany kod.

Wady:

  • Błędy w czasie działania.
  • Utrata zaufania do statycznej typizacji TypeScript.

Pozytywny przypadek

Sprawdzamy typ przed pracą z metodą:

if (typeof value === 'string') { return value.toUpperCase(); } else { return value.toString(); }

Zalety:

  • Pełne bezpieczeństwo na etapie kompilacji.
  • Poprawiona łatwość utrzymania kodu.

Wady:

  • Konieczność pisania zbędnych sprawdzeń.