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życieany
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 typeguardaany
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ówuknown
jest znacznie bezpieczniejszy odany
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.