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:
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.
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:
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
Utwórz prostą funkcję o nazwie isString, która działa jako niestandardowy strażnik typu sprawdzający, czy wartość unknown jest stringiem.
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
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'.
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
Zaimplementuj funkcję isCustomError(err: unknown): err is CustomError, która sprawdza, czy nieznana wartość jest niepustym obiektem CustomError posiadającym klucz 'code'.
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
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'.
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