Wyślij zapytanie Dołącz do Sii

Język Typescript cieszy się coraz większą popularnością zarówno wśród projektów front-end jak i back-end, gdzie do tej pory wykorzystywany był Javascript. Nie ma w tym nic dziwnego – język ten oferuje nam dużo większą przewidywalność, stabilność oraz poprawia jakość kodu i projektu. Jedną z technik potrzebnych do organizacji kodu i jego lepszego zrozumienia, którą przybliżę w artykule, jest korzystanie z Type Guard.

Type Guard

Type Guard to blok kodu, który wykorzystując zawarte w nim warunki, zwróci informacje o typie przekazanej zmiennej. Pozwala nam to zawęzić typ zmiennej i poprawne wykorzystanie jej właściwości w dalszych częściach kodu. Aby zobrazować ten problem, przyjrzyjmy się poniższemu fragmentowi kodu.

Trzy typy użytkowników
Ryc. 1 Trzy typy użytkowników

Nasza aplikacja pozwala na zalogowanie się 3 typom użytkowników:

  • Super admin,
  • Admin,
  • User.

Każdy z nich posiada wspólny typ Account oraz dedykowany i z unikalną metodą typ makeOrder, removeUser i removeAdmin:

Account Type cd.
Ryc. 2 Account Type cd.

Wygląda na to, że ma to ręce i nogi. Teraz użytkownik loguje się do naszej aplikacji i ma 3 ścieżki:

  • złożenie zamówienia,
  • usunięcie użytkownika,
  • usunięcie admina.

Stwórzmy więc funkcję, która zadecyduje o tym, jaka metoda ma zostać odpalona:

Funkcja makeAction
Ryc. 3 Funkcja makeAction

Funkcja makeAction przyjmuje parametr account o unii User, Admin i SuperAdmin. Jak widać, Typescript informuje nas o polach dostępnych na tym obiekcie, wynikających z części wspólnej typów TUser, TAdmin oraz TSuperAdmin. Co z metodami, które są dostępne na każdym z tych typów? Tutaj Typescript chroni nas przed błędnym wywołaniem metody, kiedy typ użytkownika będzie inny niż zakładamy.

Naszym zadaniem jest rozpoznanie tego typu i na jego podstawie, wywołanie odpowiedniej metody, która nie wywoła błędu w runtime.

Sposoby wyciągnięcia typu zmiennej

Istnieje 5 sposobów na wyciągnięcie typu zmiennej:

  • użycie in w celu sprawdzenia istnienia property i określenia typu,
  • użycie instanceof do określenia typu,
  • użycie typeof do określenia typu,
  • zawężanie poprzez porównanie,
  • niestandardowe, własne type guard.

In type guard

In type guard dostarcza informacji, czy obiekt, który sprawdzamy, ma w sobie określone property. Należy pamiętać, że property znajdująca się w naszym warunku, powinna być unikalna dla jednego konkretnego typu, aby zawęzić wyboru dla Typescripta do jednej opcji (chyba, że cel jest inny). Jeśli warunek zostanie spełniony, Typescript zawęża wybór z 3 różnych typów do jednego, co spowoduje możliwość wywołania konkretnej metody, na konkretny typie obiektu.

In type guard
Ryc. 4 In type guard

Warto przypomnieć, że property musi być unikalne dla danego typu. Na przykład, jeśli warunkiem do wywołania metody makeOrder jest posiadanie property name, Typescript napotka błąd, ponieważ name występuje na każdym możliwym typie na obiekcie account.

Instanceof type guard

Instanceof jest wbudowanym w Javascript operatorem, sprawdzającym czy nasza zmienna jest instancją wybranej przez nas klasy. Dzięki temu jesteśmy w stanie zweryfikować, czy nasz parametr pokrywa się z wybraną przez nas klasą i zawęzić go do interesującego nas typu. Na początku zadeklarujemy klasy, które implementują nasze typy:

Ryc. 5 Instanceof type guard
Ryc. 5 Instanceof type guard

Następnie w warunku if sprawdzamy, czy nasz parametr jest instancją danej klasy. Metoda ta opiera się na porównaniu łańcuchów prototypów (prototype chain) dla zmiennej account oraz klasy np. User. W przypadku, gdy warunek będzie spełniony i łańcuchy będą się pokrywać, Typescript pozwoli nam na skorzystanie z metod i pól dostępnych tylko na tym konkretnym typie.

Efekt jest następujący:

Instanceof type guard cd.
Ryc. 6 Instanceof type guard cd.

The typeof type guard

Typeof, tak jak instanceof, jest wbudowanym operatorem Javascript. W tym przypadku nie porównujemy obiektu do instancji danej klasy, ale porównujemy typ na jego konkretnym polu i o wyniku informujemy Typescript. Aby zilustrować ten przykład, dodajmy nowe pole na typie TAccount o nazwie email, które może przyjąć wartość string lub null, jeśli użytkownik nie wprowadził jeszcze swojego maila do bazy danych.

Po wejściu na konkretny widok użytkownik ma możliwość wysłania maila z aktualną ofertą na swoją skrzynkę internetową. Stwórzmy zatem funkcję sendEmail, która przyjmuje parametr o wartości string oraz funkcję, która wywoła tę metodę. Jak widać, Typescript słusznie nie pozwoli nam na odpalenie kodu, ponieważ na tym etapie nie wiemy, czy przekazana przez nas wartość na pewno jest stringiem i program wyrzuciłby błąd w runtime.

Funkcja sendEmail
Ryc. 7 Funkcja sendEmail

Należy zatem dodać warunek sprawdzający, czy typ na polu email jest równy string.

Funkcja sendEmail cd.
Ryc. 8 Funkcja sendEmail cd.

W ten sposób mamy pewność, że pole email zawiera email użytkownika (podlegający wcześniejszej walidacji w aplikacji) i Typescript będzie zadowolony.

Zawężanie poprzez porównanie

W przypadku, kiedy jedno z pól na naszym obiekcie ściśle określa jego typ, warto zastosować zawężenie typu poprzez porównanie jego wartości. W naszym przypadku porównamy property type do każdej z wartości enuma AccountType. W ten sposób jesteśmy w stanie zawęzić typ obiektu do jednego konkretnego, który nas interesuje.

Zawężanie poprzez porównanie
Ryc. 9 Zawężanie poprzez porównanie

Po spełnieniu warunków, Typescript daje nam dostęp do wybranych metod dla każdego z trzech typów.

Niestandardowe type guards

Ciekawym rozwiązaniem na zawężanie typów jest pisanie własnych type guards. Zaletą tej techniki, jest możliwość stworzenia własnej metody, która nie narzuca nam żadnych ograniczeń i pozwala sprawdzić kilka interesujących nas warunków, a następnie zawęzić typ do jednego konkretnego, który nas interesuje. Niestety, jest tutaj pewne ryzyko popełnienia błędu podczas pisania takiego kodu i otrzymania błędu w runtime.

Przejdźmy zatem przez konkretny case, kiedy możemy tej techniki użyć. Dodajemy nowe pole, które przyjmuje wartość boolean o nazwie isAdult, na type TUser. Następnie dodajemy kolejny typ Tadult, który musi posiadać wartość isAdult równą true, oraz metodę adultAction dostępną tylko na tym typie.

Pole isAdult
Ryc. 10 Pole isAdult

Następnie stwórzmy metodę makeOnlyAdultAction, która ma za zadanie wywołać metodę adultAction, tylko dla osoby pełnoletniej.

mekOnlyAdultAction
Ryc. 11 mekOnlyAdultAction

Jak widać, Typescript wyrzuca nam błąd, ponieważ nie każdy zalogowany użytkownik, może być dorosły i nie jest w stanie wywołać tej metody.

Aby sprawdzić, czy dana osoba jest pełnoletnia, oraz aby Typescript wiedział, że może wywołać wyżej wymienioną metodę, stwórzmy więc nową metodę, która zawęzi nam typ TUser do typu TAdult.

Ryc. 12 isAdultUser
Ryc. 12 isAdultUser

Wyżej umieszczony kod zawiera 2 warunki:

  • czy property type jest równe User,
  • czy flaga isAdult jest równa true.

Jeśli warunek zostanie spełniony, typ, który przekazaliśmy do metody, zostanie przez Typescript zawężony do typu TAdult. Sprawdźmy, jak to wygląda w praktyce.

makeOnlyAdultAction cd.
Ryc.13 makeOnlyAdultAction cd.

Podsumowanie

W artykule przedstawiłem kilka metod na zabezpieczenie swojego kodu i wzmocnienie bezpieczeństwa typów w naszym kodzie, dzięki czemu jest on bardziej stabilny, przewidywalny i częściowo przetestowany.

Należy pamiętać, że każda linijka ma swoją cenę. Dodanie type guards w wielu miejscach naszej aplikacji z pewnością przyniesie liczne korzyści w przyszłości, ale czas poświęcony na pokrycie naszego kodu type guardami będzie odczuwalny i wymaga dobrego przemyślenia i zaprojektowania typów/interfejsów. Projekt projektowi nierówny, lecz uważam, że gra jest warta świeczki.

5/5 ( głosy: 7)
Ocena:
5/5 ( głosy: 7)
Autor
Avatar
Filip de Tillier

Absolwent Politechniki Gdańskiej. W 2017 roku rozpoczął swoją karierę zawodową głównie jako Front-end developer. Ma również doświadczenie jako Back-end developer z użyciem języka JavaScript oraz Python. Aktualnie skupia się nad rozwojem w bibliotece React. Jego pasją jest sport i F1.

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Może Cię również zainteresować

Pokaż więcej artykułów

Bądź na bieżąco

Zasubskrybuj naszego bloga i otrzymuj informacje o najnowszych wpisach.

Otrzymaj ofertę

Jeśli chcesz dowiedzieć się więcej na temat oferty Sii, skontaktuj się z nami.

Wyślij zapytanie Wyślij zapytanie

Natalia Competency Center Director

Get an offer

Dołącz do Sii

Znajdź idealną pracę – zapoznaj się z naszą ofertą rekrutacyjną i aplikuj.

Aplikuj Aplikuj

Paweł Process Owner

Join Sii

ZATWIERDŹ

This content is available only in one language version.
You will be redirected to home page.

Are you sure you want to leave this page?