Zawężanie typu (Type Narrowing) w TypeScript to proces, w którym kompilator "rozumie", że w danym fragmencie kodu, zmienna jednoznacznie ma bardziej konkretny typ, na podstawie warunków.
Typowe techniki zawężania:
typeof:function example(x: number | string) { if (typeof x === 'string') { // x: string tutaj return x.toUpperCase(); } else { // x: number tutaj return x.toFixed(2); } }
instanceof (dla klas):if (dateObj instanceof Date) { // dateObj: Date }
null i undefined:function print(value?: string) { if (value != null) { // value: string console.log(value.length); } }
type Pet = { kind: 'dog'; woof: () => void } | { kind: 'cat'; meow: () => void }; function sound(pet: Pet) { if (pet.kind === 'dog') { pet.woof(); } else { pet.meow(); } }
TypeScript obsługuje również niestandardowe funkcje typu strażnika:
function isString(x: unknown): x is string { return typeof x === 'string'; }
Zawężanie sprawia, że sprawdzenia typów są bezpieczniejsze, a kod — bardziej niezawodny.
Czy można zagwarantować zawężanie typu przez zwykłe porównania (np.
==/===), i czy zawsze działa?
Odpowiedź: Nie. Nie we wszystkich przypadkach TypeScript rozumie typ z prostych porównań, szczególnie jeśli porównanie jest zbyt "niejasne" lub zachodzi przez pośrednie zmienne/właściwości. Aby zwiêkszanie często należy stosować wyraźne mechanizmy (typeof, instanceof, właściwości dyskryminacyjne i strażnicy typów).
Przykład:
function foo(x: number | string | null) { if (x) { // x: string | number, null już nie jest możliwy, ale zawężenie do konkretnego nie będzie } }
Historia
W dużym projekcie TypeScript warunek typu user.role == 'admin' nie zawęził typu danych, i wciąż było konieczne pisanie sprawdzeń dotyczących istnienia właściwości. Programiści niedoszacowali zasady zawężania, co doprowadziło do błędów "Cannot read property ... of undefined".
Historia
W aplikacji mobilnej funkcja przyjmowała albo obiekt, albo string. Poprzez pośrednie wywołanie funkcji zmieniającej typ, zawężenie nie miało miejsca, i na niektórych urządzeniach wystąpił błąd przy wywołaniu metody, która nie istniała dla stringa. Testy na rzadkich przypadkach nie powiodły się.
Historia
Podczas migracji kodu z JavaScript do TypeScript nie zaimplementowano swoich funkcji strażników typów, zakładając, że sprawdzenia właściwości zawsze zawężą typ. W rezultacie złożone obiekty z opcjonalnymi polami zachowały się nieprawidłowo, a w runtime wystąpiły nieprzewidywalne błędy dostępu do danych.