Backend

Jak TypeScript i analiza statyczna pomagają w automatycznym znajdowaniu bugów

Osobiście uważam, że TypeScript to jedna z najlepszych rzeczy jakie spotkały JavaScriptową społeczność. Otrzymaliśmy statycznie typowany język zbudowany na składni, którą wszyscy już doskonale znają i dodatkowo jest wspierany przez potężne firmy jak Microsoft i Google. W tym artykule chcę poruszyć kwestie tego, w jaki sposób używanie TS i analizy statycznej kodu pozwala zminimalizować ilość bugów w kodzie.


Krzysztof Kaczor. Lead Dev w Neufund. Aktualnie pracuje w branży kryptowalutowej w Berlinie. W przeszłości psychofan JavaScriptu, aktualnie pisze głównie w TypeScripcie. Zafascynowany budową kompilatorów i analizą statyczną języków programowania.


Strict mode, czyli “rygorystyczny” TypeScript

-—strict to flaga kompilatora typescript (tsc), która włącza wiele pomniejszych flag wspomagających pisanie poprawnego kodu. Jeżeli zaczynasz nowy projekt to ZAWSZE powinieneś jej używać, strict mode mocno zmienia zachowanie języka.

Null referencje

Sławny “Błąd wart miliard dolarów”, czyli null referencje jest rozwiązany w TS poprzez wprowadzenie wymogu jasnego oznaczania zmiennych, które mogą zawierać null albo undefined. Używamy do tego union typów a | null | undefined albo w przypadku parametru funkcji możemy oznaczyć go jako opcjonalny. Spójrzmy na przykład:

interface IOptions {
        db?: {
                    host: string;
                    user: string;
                    pass: string;
        }
}
function connectDB(options: IOptions) {
        if (!options.db) {
                    throw new Error("DB credentials missing");
 }
        // tutaj kompilator jest pewny, że options.db nie jest nullem / undefined (falsy)
        options.db;
}

No implicit any

TypeScript, w przeciwieństwie do tworzonego przez Facebooka Flow Type, nie próbuje inferować (“zgadywać”) typów tam, gdzie wymaga to zdecydowanie większego wysiłku np. w sygnaturach funkcjach na podstawie użycia zmiennych w ciele. Spójrzmy na konkretny przykład:

function sum(a, b) {

 return a + b;

}

Flow wyinferuje sygnaturę funkcji jako mniej więcej: (a: string | number, b: string | number): string | number, co jest prawdopodobnie błędem — paramertry funkcji powinny być typu number. Domyślnie, TypeScript stwierdzi typ any, który oznacza po prostu dowolną wartość. Jest to jeszcze gorsze zachowanie, ale na szczęście, w przypadku użycia strict mode, kompilator za każdym razem, kiedy wyinferowałby typ any zwróci po prostu… błąd. Wbrew pozorom to bardzo dobre zachowanie — programista po prostu dostaje czytelną informację o konieczności jasnego podania typu. Dzięki temu unikniemy błędów, kiedy to type checker próbuje być “zbyt mądry”.

Statyczna analiza za pomocą TSLint

No dobrze, pokryliśmy parę sytuacji, kiedy type checker wbudowany w kompilator może uratować naszą skórę, ale czy możemy łatwo znaleźć inne klasy błędów? Oczywiście, że tak — wystarczy, że skorzystamy z TSLinta, narzędzia przeprowadzającego analizę statyczną kodu.

TSlint podobnie jak ESlint korzysta z reguł, które użytkownik sam musi zdefiniować. Oczywiście istnieją gotowe zestawy reguł, które można “wciągnąć” do swojego projektu w parę sekund. W tym miejscu chciałbym polecić swój projekt o nazwie TypeStrict. Jest to ruleset, który dba tylko i wyłącznie o znajdowanie błędów w kodzie użytkownika i totalnie ignoruje formatowanie kodu (gdzie mamy dostępne lepsze narzędzia takie jak prettier).

No więc jakie błędy potrafi znaleźć TSLint wraz z TypeStrictem? Pracując w JavaScripcie, na pewno nie raz miałeś do czynienia z promisami. Nie tak dawno temu otrzymaliśmy nawet składnie dedykowaną ku temu zadaniu await/async. Więc praca z promisami staje się w naprawdę przyjemna w TS. Jednak pojawia się problem, kiedy zapomnimy obsłużyć jakiegoś promisa, np:

async function asyncWork() {

        // zrób coś asynchronicznego

}

main() {

 asyncWork();

        console.log("done")

}

W powyższym przykładzie “done” nie tylko zostanie wypisane w złym momencie (przed ukończeniem pracy asyncWork, ale jeśli asyncWork rzuci wyjątek, tzn. promise zostanie zrejectowany to nie dostaniemy o tym żadnej informacji. Jest to bardzo zła sytuacja. Na szczęście, reguła no-floating-promises zawarta w TypeStricticie uchroni nas dokładnie przed takimi wydarzeniami.

Kolejna sytuacja, kiedy analiza statyczna może uratować naszą skórę powstaje np. kiedy kompilator wyinferuje typ {}, czyli ogólny typ bez żadnych ograniczeń. Praktycznie zawsze jest to zwiastunem błędu, dlatego TypeStrict poinformuje nas o tym.

Oprócz tego w rule secie znajduje się około 30 innych przydatnych reguł .

Podsumowanie

TypeScript daje różnym narzędziom o wiele większe pole do popisu ponieważ więcej informacji jest dostępnych podczas kompilacji. To pozwala na stworzenie zaawansowanej analizy statycznej kodu, niedostępnej z poziomu czystego JavaScriptu. Jestem ciekaw Waszych doświadczeń z TypeScriptem, dlatego chętnie przeczytam komentarze pod artykułem.


baner

Zdjęcie główne artykułu pochodzi z pexels.com

Podobne artykuły

[wpdevart_facebook_comment curent_url="https://geek.justjoin.it/typescript-analiza-statyczna-pomagaja-automatycznym-znajdowaniu-bugow/" order_type="social" width="100%" count_of_comments="8" ]