Miniatura artykułu

Unknown vs any. Czym się od siebie różnią?

8 minut

Skopiuj link

Data publikacji: 7/19/2023, 5:39:37

Ostatnia aktualizacja: 4/1/2024

Wstęp

Dla większości początkujących użytkowników TypeScriptu typ any jest już doskonale znany. Zazwyczaj służy do omijania problemów, które nie powinny być w ten sposób ominięte, a co za tym idzie, często jest stosowany niezgodnie z przeznaczeniem. Za chwilę dowiesz się jak poprawnie go wykorzystać i kiedy tak naprawdę powinniśmy go stosować.

Jeżeli chodzi typ unknown, to na początku nauki jest on omawiany bardzo pobieżnie lub w ogóle pomijany ponieważ nie używa się go tak często jak innych typów. Dodatkowo niezrozumienie, lub niepełne zrozumienie sprawia, że trudniej jest się przełamać i zacząć nim swobodnie (i poprawnie) operować.

Na początek omówimy te dwa typy oraz ich zastosowanie, a na końcu porównamy je, żeby jeszcze wyraźniej podkreślić różnice i podobieństwa pomiędzy nimi.

Any

Zacznijmy od any. Jest to typ, który "wyłączą" wszelkie korzyści płynące z zastosowania TypeScriptu (między innymi statyczną kontrolę typów). Wszystko, co zostanie otypowane w ten sposób może przyjąć dosłownie każdą(!!) wartość, która istnieje w TS. Jednocześnie przestaniemy otrzymywać jakiekolwiek podpowiedzi ze strony edytora kodu i stracimy całe bezpieczeństwo związane ze statycznym typowaniem. Należy unikać używania any za wszelką cenę. Często oznacza to nieco więcej pracy, która jednak zaowocuje w przyszłości łatwiejszym w utrzymaniu kodem. Więcej zalet TypeScriptu oraz podstawy jego działania wyjaśniam w tym artykule.

Nasuwa się pytanie, czy w takim razie any jest zawsze złe i nie powinniśmy go nigdy używać?

Odpowiedź to oczywiście nie. Użycie tego typu nie zawsze jest niepoprawne, istnieje kilka przypadków, w którym powinniśmy po niego sięgnąć:

  • zmiana kodu JS w TS - w momencie w którym decydujemy się na migrację projektu napisanego w JavaScript do TypeScript mamy do otypowania niemal wszystko. Bardzo trudno jest to zrobić w jednym momencie (chyba, że projekt jest naprawdę mały), dlatego możemy użyć any w miejscach, które będą otypowane w przyszłości.

  • w innym typie - przykładem może być typ opisujący dowolny obiekt lub dowolną funkcję. Czasami tworzymy funkcję, która po prostu musi otrzymać obiekt, nie jest ważne jaki, ponieważ w żaden sposób w niego nie ingerujemy (możemy na przykład dodawać do niego metodę, zapisywać go w formacie JSON, itd.). Co prawda w przykładzie poniżej możemy również zastosować unknown, jednak użycie any nie jest tutaj niebezpieczne, dopóki nie pracujemy z właściwościami lub metodami tego obiektu.

Zdecydowanie zalecam również zastosować w pliku konfiguracyjnym TS (tsconfig.json) opcję noImplicitAny: true. Dzięki niej TS wymusi na nas dodanie typów do zmiennych (argumentów funkcji, właściwości itd.), dla których zainferowany typ to any.

Unknown

Do tego typu również możemy przypisać dowolną wartość, nie ma żadnych ograniczeń. Jest on jednak znacznie bezpieczniejszy w zastosowaniu, ponieważ nie powoduje wyłączenia ochrony zapewnianej przez TypeScript. Co przez to rozumiem?

Przede wszystkim TS nie pozwoli nam operować na wartościach, co do których nie ma pewności, czy istnieją. Jesteśmy zmuszeni upewnić się z jakim typem pracujemy.

Sprawa wygląda podobnie dla każdego innego typu - jeżeli chcemy zastosować np. metody stringa, to musimy się upewnić, że pracujemy ze stringiem.

Innymi słowy TS wymusza na nas zastosowanie typeguarda. Jeżeli nie spotkałeś się wcześniej z tym określeniem, to napisałem na ten temat osobny artykuł.

Jak sama nazwa wskazuje typ ten możesz zastosować w miejscu, w którym nie jesteś w stanie przewidzieć, z jaką wartością masz do czynienia. Przykładem może być funkcja wykonująca zapytanie do zewnętrznego API na które nie mamy wpływu i nie możemy przewidzieć, czy dane, które otrzymujemy nie ulegną zmianie.

Zdecydowanie zachęcam również do zastosowania flagi useUnknownInCatchVariables: true w pliku konfiguracyjnym TS. Dzięki niej, wszystkie błędy przechwycone przez blok catch będą miały domyślnie typ unknown zamiast any, co znacząco podniesie bezpieczeństwo podczas pracy z nimi.

Any vs Unknown

Na pierwszy rzut oka oba te typy wydają się podobne - jeden i drugi oznacza dowolną wartość. Żeby poprawnie zastosować jeden z nich, trzeba pamiętać o kilku różnicach:

  • Używając any tracimy wszystkie zalety wynikające z używania TypeScriptu. W przypadku unknown, TS wymusi na nas zastosowanie typeguarda

  • any powinien być stosowany głównie podczas przepisywania kodu JavaScript na TypeScript, natomiasta unknown stosujemy, gdy nie wiemy jakich danych możemy się spodziewać i ustalimy to dopiero w czasie runtime'u z wykorzystaniem typeguardów

  • uknown jest znacznie bezpieczniejszy od any ponieważ nie wyłącza statycznej kontroli typów

Typ unkown można odebrać jako: "nie wiem, dlatego będę z tym ostrożny", natomiast any to raczej: "nie wiem i mnie to nie interesuje".

Podsumowanie

Największą zaletą TS jest statyczna kontrola typów, a co za tym idzie, znacznie bezpieczniejszy kod. Użycie any w nieprawidłowy sposób, może sprawić, że część napisanego przez nas kodu nie będzie tak bezpieczna, jak byśmy tego chcieli.

Co więcej, może to wpłynąć na istniejący już kod. Jeżeli utworzona przez nas funkcja zwróci wartość typu any, to kod, który z niej korzysta również utraci część statycznej kontroli.

Pamiętaj też, że unie typów, które zawierają any, automatycznie stają się typem any. Jeżeli ktoś z kim pracujemy postanowi zbudować typ na podstawie niepoprawnie otypowanej funkcji, zmiennej, itd. to może się okazać, że spędzi sporo czasu na poprawianiu naszych niedociągnięć.

Warto poświęcić więcej czasu na poprawne otypowanie wartości i zaoszczędzić sobie, oraz innym pracy w przyszłości.

Z kolei uknown nie wpływa aż tak negatywnie na kod, jednak podobnie jak any, nie powinien być stosowany w celu pominięcia problematycznego typowania.

Używaj go jedyne wtedy, kiedy faktycznie nie jesteś w stanie określić typu z którym pracujesz (zewnętrzne API, błąd złapany w bloku catch, itp.).

W przypadku stosowania tego typu łatwo o tak zwane defensywne programowanie (defensive programming) i przesadną liczbę typeguardów.

Gdyby w Twojej głowie zrodziły się jakieś pytanie, to zachęcam do zadania ich w komentarzu, postaram się odpowiedzieć najszybciej, jak to możliwe.

Avatar: Wojciech Rygorowicz

Software Engineer / Fullstack developer

Wojciech Rygorowicz

wojciech.rygorowicz@gmail.com

Podziel się na

Dodaj komentarz

Komentarze (0)

Brak komentarzy