Problem czytelności w TypeScript - jak go rozwiązać?
- Czas potrzebny na przeczytanie:5 minut
- Opublikowane:
Serio, uwielbiam ten język. Nie wyobrażam sobie pracy w czystym JavaScripcie, ale jest kilka rzeczy, które mnie wkurzają w TS. Jedną z większych jest czytelność. Tak, coś co miało być zaletą, jest jednocześnie dużym problemem. Jak pisać czytelniejszy kod w TypeScript?
Czytelność
Zacznijmy z grubej rury i zobaczymy przykładową funkcję w Haskellu:
hasPath :: [(Int, Int)] -> Int -> Int -> Bool
hasPath [] x y = x == y
hasPath xs x y
| x == y = True
| otherwise =
let xs' = [ (n,m) | (n,m) <- xs, n /= x ]
in
or [ hasPath xs' m y | (n, m) <- xs, n == x ]
Nic nie ogarniasz? Nie musisz. Osoby, które są zaznajomione z tym językiem są "przyzwyczajone" do nazywania niektórych rzeczy np. w ten sposób: xs
.
Zanim wszyscy popukamy się w głowę i wyjdziemy z pochodniami na ulicę, żeby przepędzić entuzjastów programowania funkcyjnego, wróćmy na nasze rodzime podwórko.
document.addEventListener('click', (e) => {});
JavaScript Developerzy są przyzwyczajeni do przekazywania e
zamiast event
i większość z nas nie ma z tym problemu. Dochodzimy więc do wniosku, że czytelność to tak naprawdę rzecz umowna, uzelażniona od pewnych przyzwyczajeń, nie jest uniwersalna.
W dalszej części artykułu skupiam się bardziej na przyjętych w środowisku JS/TS "dobrych praktykach" i niestosowaniu się do nich w pewnych częściach języka.
Typy generyczne
W codziennej pracy staramy się nazywać odpowiednio funkcje, obiekty i zmienne, tak, aby drugi programista wiedział co w danej linijce mieliśmy na myśli. W tym całym "czystym kodzie" zapominamy często o typach! Widoczne to jest najczęściej w typach generycznych, gdzie argument deklarujemy jako <T>
. Ta praktyka została zapożyczona z C# i... po prostu tak zostało.
O ile w prostych przykładach możemy tego nie dostrzegać, tak przy bardziej zaawnsowanych, często warunkowych/mapowanych typach, problem znacząco się nasila...
Powyższe, skromne pięć linijek kodu może przyprawić niejednego programistę o ból głowy. Pomijając już samą "logikę" tego typu, to c'mon, kto w normalnych funkcjach nazywałby tak swoje argumenty? Prosta zmiana nazw i kod staje się nieco bardziej zrozumiały:
Oczywiście, jak to już bywa w naszej branży, od każdej reguły istnieją jakieś wyjątki, nic nie jest czarno-białe. Dla mnie takim "marginesem błędu" jest tworzenie ogólnych, często mapowanych typów. Zresztą wbudowane, pomocnicze typy zostały właśnie w ten sposób zaimplementowane przed twórców samego języka:
type Partial<T> = { [P in keyof T]?: T[P] | undefined };
Newsletter dla Frontend Developerów 📮
Błędy
Niech pierwszy rzuci kamieniem ten, który nie złapał się za głowę widząc błąd z TypeScripta. Nie ma to, jak dostać na klatę errorem na pół ekranu...

Na przestrzeni lat powstała nawet niepisana (bardzo przydatna) porada dla początkujących - "Czytaj tylko ostatnią linijkę błędu". Na szczęście na rynku pojawiają się takie perełki jak TypeScript Error Translator, które przyjmują wypluty błąd i przekształcają go na zrozumiałą dla człowieka informację.

Twórca tego narzędzia przygotował nawet specjalne rozszerzenie do VSCode, nic tylko brać, czyste złoto!
Prefixy
A na sam koniec mały, subiektywny bonusik. Otóż praktyką, którą również zapożyczyliśmy z innych języków jest dodawania I
lub T
do analogicznie interfejsów i typów. Czy programiści naoglądali się za dużo Impl
w Javie, czy też po prostu uznali, że jednoznakowy prefix będzie dobrym materiałem na konwencję nazewniczą?
Części osób skojarzy się to na pewno z nieco prehistoryczną już notacją węgierską. Ta konwencja nazewnicza została oryginalnie wprowadzona, by adresować typ danego wyrażenia. C
od klasy, A
od klas abstrakcyjnych, s
od ciągu znaków itp. Przekładając to na nasze, programistyczne dinozaury na pewno pamiętają $
z jQuery.
Kolejna sprawa, czyli lenistwo programistów. Nie ukrywajmy, nazywanie rzeczy w programowaniu nie należy do najłatwiejszych. Korzystanie z prefixów ułatwia bardzo sprawę, ale może warto czasem się na chwilę zatrzymać i pokminić nad lepszą nazwą? :)
Żeby nie było, że tylko hejtuję. Sam uciekam się często w pewnym sensie o odwróconą, wspomnianą Impl
w Javie, tylko że w React, deklarując propsy:
interface ComponentProps {}
const Component = () => {};
Jednak to dla mnie trochę coś innego niż prosty prefix/postfix. W tym przypadku faktycznie mówimy, czym dany interfejs jest, nie uciekając się do przyzwyczajeń z I
.
Chociaż wspomniana technika do mnie nie przemawia, to z konwencjami nazewniczymi jest trochę tak, że jakby zła ona nie była, to i tak ważniejsza jest konsekwencja z kodzie...
Podsumowanie
TypeScript, przecież miało być czytelniej... a wyszło jak zawsze, czyli tak średnio.
Na szczęście są na to sposoby i z małą pomocą programistów, niektóre problemy mogą odejść w zapomnienie.