Przejdź do głównej treści
eLearner.app
Moduł 5 · Lekcja 1 z 29/14 w kursie~15 min
Lekcje modułu (1/2)

Strażnicy Typów

Zawężanie typów (Type Narrowing) to jeden z najważniejszych konceptów w TypeScript. Często jednak standardowe strażniki typów (type guards), takie jak typeof czy instanceof, nie wystarczają do obsługi złożonych obiektów niestandardowych. Właśnie tutaj z pomocą przychodzą Niestandardowe Strażniki Typów (Custom Type Guards).


Standardowe Strażniki Typów

Zacznijmy od podsumowania sposobu, w jaki TypeScript zawęża typy za pomocą standardowych operatorów JavaScriptu:

TS
function processInput(val: string | number) {
  if (typeof val === 'string') {
    // Qui 'val' è di tipo string
    console.log(val.toUpperCase());
  } else {
    // Qui 'val' è di tipo number
    console.log(val.toFixed(2));
  }
}

Niestandardowe Strażniki Typów (operator is)

Aby zdefiniować niestandardowy strażnik typu, tworzymy funkcję, która zamiast standardowego logicznego typu zwracanego (boolean) zwraca predykat typu w formacie parameterName is Type.

Funkcja musi zwracać wartość logiczną (true lub false). Jeśli zwróci true, TypeScript będzie wiedział, że przekazany parametr jest określonego typu.

TS
interface Cat {
  name: string;
  meow(): void;
}

interface Dog {
  name: string;
  bark(): void;
}

// Questa è una guardia di tipo personalizzata
function isCat(animal: Cat | Dog): animal is Cat {
  return (animal as Cat).meow !== undefined;
}

function makeNoise(pet: Cat | Dog) {
  if (isCat(pet)) {
    // Qui TypeScript sa che 'pet' è un Cat
    pet.meow();
  } else {
    // Qui TypeScript sa che 'pet' è un Dog
    pet.bark();
  }
}

Bezpieczeństwo a Rzutowanie Typów (as vs Strażniki Typów)

Rzutowanie typów (np. val as Cat) zmusza kompilator do zaufania programiście bez jakiejkolwiek rzeczywistej weryfikacji w czasie wykonywania (runtime). Jeśli obiekt w czasie wykonywania nie spełni oczekiwań, kod po cichu zawiedzie lub zgłosi wyjątek.

Niestandardowe strażniki typów pozwalają natomiast na przeprowadzenie solidnej i dynamicznej kontroli w czasie wykonywania, bezpiecznie i precyzyjnie informując kompilator TypeScript o rzeczywistym typie zmiennej:

TS
function processInput(input: unknown) {
  // casting insicuro: potrebbe rompersi se input non ha il metodo split
  // const str = input as string;
  // console.log(str.split(' '));

  // guardia sicura
  if (isString(input)) {
    console.log(input.split(' ')); // Sicuro al 100%!
  }
}

Spróbuj sam

Ćwiczenie 1: Strażnik Typu dla Ciągów Znaków

Ćwiczenie#ts.m5.l1.e1
Próby: 0Ładowanie...

Utwórz prostą funkcję o nazwie isString, która działa jako niestandardowy strażnik typu sprawdzający, czy wartość unknown jest stringiem.

Ładowanie edytora...
Pokaż wskazówkę

Typem zwracanym przez funkcję musi być val is string, a do sprawdzenia, czy wartość jest równa 'string', należy użyć typeof.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 2: Strażnik Typu dla Użytkownika Premium

Ćwiczenie#ts.m5.l1.e2
Próby: 0Ładowanie...

Mając dane typy User i PremiumUser, napisz funkcję strażnika typu isPremiumUser(user: User): user is PremiumUser, która sprawdza, czy użytkownik ma rolę 'premium'.

Ładowanie edytora...
Pokaż wskazówkę

Zdefiniuj sygnaturę jako function isPremiumUser(user: User): user is PremiumUser i porównaj user.role z 'premium'.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 3: Weryfikacja Niestandardowego Obiektu Błędu

Ćwiczenie#ts.m5.l1.e3
Próby: 0Ładowanie...

Zaimplementuj funkcję isCustomError(err: unknown): err is CustomError, która sprawdza, czy nieznana wartość jest niepustym obiektem CustomError posiadającym klucz 'code'.

Ładowanie edytora...
Pokaż wskazówkę

Sprawdź najpierw, czy typeof err jest równy 'object' i nie jest nullem, a następnie użyj operatora 'in', aby sprawdzić obecność właściwości 'code'.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 4: Strażnik Typu Admin

Ćwiczenie#ts.m5.l1.e4
Próby: 0Ładowanie...

Mając dane dwa interfejsy: User (z właściwością role: string) i Admin (który rozszerza User i ma właściwość adminToken: string), napisz funkcję strażnika typu o nazwie isAdmin, która przyjmuje obiekt user typu User i zwraca predykat typu user is Admin. Strażnik musi sprawdzać, czy rola użytkownika (role) jest równa 'admin'.

Ładowanie edytora...
Pokaż wskazówkę

Zdefiniuj sygnaturę jako function isAdmin(user: User): user is Admin i sprawdź, czy user.role === 'admin'.

Rozwiązanie dostępne po 3 próbach