TypeScript – pierwsze kroki. Tworzenie klas w TypeScript. Część Pierwsza.

Kilka postów temu pokazałem jak w TypeScripcie tworzyć własne typy przy użyciu interfejsów i enumeratorów. Teraz czas na to aby stworzyć własny typ przy użyciu klas. Jest to zdecydowanie jeden z najpopularniejszych sposobów na tworzenie typów w TS’ie i chyba jeden z najczęściej używanych mechanizmów w tym języku w ogóle.

Prawda jest taka, że tworzenie klas nie jest cechą unikatową dla TS, a częścią specyfikacji EcmaScript6. Jednak w połączeniu z opcjami, które daje nam TS, klasy stają się naprawdę potężnym narzędziem służącym do tworzenia obiektowego kodu.

Tworzenie klas w TypeScript

Jest jedna rzecz, o której muszę napisać już na samym początku. Ta część ES6, która dodaje do JavaScriptu możliwość tworzenia klas, to tylko upiększenie składni. Nie zmienia się w żaden sposób działanie języka. W środku to wciąż kod obiektowy bazujący na prototypach. Jednak dzięki klasom można korzystać z bardziej przejrzystych (według mnie) konstrukcji.

Aby stworzyć nową klasę wystarczy użyć operatora class, podać nazwę tworzonej klasy (tradycyjnie z wielkiej litery) oraz otworzyć parę nawiasów ‚wąsatych’:

I to wystarczy. W ten sposób stworzona została nowa klasa w TypeScript 🙂 . Póki co nie ma ona jednak żadnej funkcjonalności. pierwszą rzeczą, którą trzeba by było dodać to konstruktor. Konstruktor to specjalna metoda, która używana jest do tworzenia instancji klasy.

Teraz mogę stworzyć obiekt, który bazuje na klasie dog:

jak widać zmienna maja zawiera teraz instancję nowej klasy. Nie mogłaby przetrzymywać innej informacji, ponieważ jest typu Dog.

Wygląda to całkiem nieźle, ale wciąż klasa ta nie ma w sobie żadnej konkretnej logiki. Przydałoby się jeszcze coś dodać. Pierwszą rzeczą, która przychodzi do głowy to pola w klasie, które można byłoby ustawić podczas tworzenia obiektu.

Pola można deklarować wewnątrz klasy, tak jak normalne zmienne. Każda instancja klasy będzie miała własną kopię takiego pola:

Klasa otrzymała dwa pola typu string: name oraz state. Domyślnie pole name jest puste a state otrzymuje wartość happy.

W taki sam sposób mogę dodawać metody do klasy:

Jak widać, przy okazji tworzenia metod nie potrzeba używać słówka function. Po nazwie metody wpisuję nawiasy, które mogą zawierać ewentualne argumenty. Następnie po dwukropku typ zwracanej wartości i blok funkcyjny. Aby dostać się do zawartość jakiegoś pola klasy, trzeba użyć słówka this, tak jak w normalnym JavaScript.

Teraz mogę udoskonalić mój konstruktor. Najlepiej byłoby, gdyby przyjmował jako argument wartość, którą mogę przypisać do pola klasy. Mogę zrobić to w taki sposób:

Nic prostszego. W konstruktorze wpisuje argument oraz jego typ i przypisuję do pola name klasy.

Ale mam w zanadrzu jeden mały trick TypeScriptowy, który jeszcze bardziej usprawni ten kod. Jeżeli do argumentu w konstruktorze dopiszę słówko public, nie będę musiał nigdzie deklarować tego pola ani wykonywać żadnych przypisań. Zamiast tego TS sam doda do klasy pole o nazwie równej nazwie parametru oraz o wartości takiej jaka zostanie przekazana w konstruktorze:

Ten kod nie zgłosi żadnego błędu i wyloguje co trzeba. Pomimo tego, że nigdzie nie zadeklarowałem pola name i nie przypisałem do niego wartości. Prawda, że sprytne? 🙂

Dodawane w ten sposób do klasy pola i metody, będą kopiowane dla każdej jej instancji. Innymi słowy, każdy obiekt utworzony na bazie klasy, będzie miał swoją wersję pól i metod. Czasem jednak chcemy aby było inaczej, aby metoda lub (rzadziej) pole, było dzielone dla wszystkich instancji. W czystym JSie nie jest łatwo tego dokonać. Najłatwiejszy sposób na osiągnięcie tego to użycie zmiennych globalnych. Mało eleganckie rozwiązanie.

Na szczęście w ES6 (a co za tym idzie w TypeScript), mamy możliwość tworzenia statycznych elementów w klasach. Statyczne pole lub funkcja, będą „dzielone” przez wszystkie instancje. Do statycznego elementu trzeba odwoływać się używając nie słówka this a nazwy klasy. Oto przykład:

Do klasy Dog dodałem dwa pola typu liczbowego o nazwie id. Jedno jest statyczne (dzielone pomiędzy instancjami) a jedno „normalne” (unikatowe dla każdej instancji). Na początku wartość statycznego pola jest równa zero. Zwykłe id nie otrzymuje w wartości. Dopiero w konstruktorze przypisuje mu wartość aktualnego statycznego ID, które zwiększam najpierw o jeden. W ten sposób każdy nowy obiekt będzie miał pole id z wartością o jeden większą niż poprzedni. Wystarczy spojrzeć na to co logują przykłady.

W taki sam sposób mogę stworzyć statyczną metodę:

W tym przykładzie dodałem metodę printBark, która będzie ‚sklejać’ łańcuch znaków zwracany w metodzie bark. W jej wnętrzu nie mogę odnosić się do kontekstu klasy, ponieważ jest to metoda statyczna. Dlatego dane opisujące instancje będą przekazywane do niej w argumentach wewnątrz metody bark. Tak samo jak w przypadku pól statycznych, printBark wywołuję nie przez this a przez nazwę klasy czyli Dog.

I to tyle na dziś. Wiadomo już jak tworzyć nowe klasy przy użyciu konstruktorów i jak dodawać do nich metody i pola (zarówno zwyczajne jak i statyczne). W kolejnym poście pokaże jak działają gettery i settery oraz jak zmieniać dostępność pól i metod wewnątrz klasy.

Jeżeli chcesz być na bieżąco z postami na blogu zachęcam do polubienia mojej strony na facebooku. Zawsze zamieszczam tam informacje o wszystkich nowościach. Jest to też dobre miejsce na kontakt ze mną. Na wszystkie pytania zawsze odpowiem :).

2 thoughts on “TypeScript – pierwsze kroki. Tworzenie klas w TypeScript. Część Pierwsza.”

  1. Świetny wpis 🙂

    Mam tylko jedną wątpliwość: czy w konstruktorze nie powinno się zawsze zaczynać od super(); ?

    Na wypadek gdyby np. klasa dziedziczyła po innej klasie.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *