W ostatnim poście pokazałem jak korzystać z podstawowych typów danych w TypeScript. Omówiłem takie typy jak string, number i boolean. Wspomniałem też o rzadziej używanych any oraz void.
Dziś pokażę jak tworzyć własne typy, w końcu większość programów w JS składa się przede wszystkim z obiektów, które tworzymy sami.
Wśród typów danych, które wymieniałem w ostatnim poście nie pojawił się typ obiektu. Może to się wydawać dziwne, w końcu to najpopularniejszy rodzaj danych jaki można spotkać w JavaScripcie. Spokojnie, jeżeli zwróciliście na to uwagę, to bardzo dobrze.
Obiekty są typami tworzonymi przez programistów. Możemy dla każdego rodzaju obiektu stworzyć osobny typ. Są trzy sposoby na utworzenie nowego typu: interfejsy, enumeratory oraz klasy. Dziś przedstawię dwa pierwsze podejścia. Na zagadnienie klas poświęcę osobny wpis.
Interfejsy
Interfejsy powinny być znajome każdemu kto programował w takich językach jak Java lub C#. W dużym uproszczeniu, są to szablony, do których przypasowywane są później obiekty. Opisują one dane oraz zachowania obiektów, które są na nich wzorowane. To może brzmieć tak jakbym mówił o klasie, ale to nie do końca to samo. Interfejs jest tylko szablonem, sam w sobie nie posiada żadnej logiki. Myślę, że najlepiej będzie pokazać to na przykładzie. Oto jak wygląda prosty interfejs w TypeScripcie:
1 2 3 4 |
interface Dog { name: string; age: number; } |
W taki sposób sposób stworzyłem interfejs Dog, teraz mogę tworzyć dane o takim typie:
1 2 3 4 5 6 |
var maja: Dog; maja = { name: 'maja', age: 5, }; |
Aby zmienna maja była poprawnie przypisana do typu Dog, musi posiadać dwa pola name o typie string oraz age o typie number. Jeżeli pól będzie za mało, lub za dużo, lub nie będą zgadzać się ich typy, edytor zgłosi błąd.
I to właściwie tyle jeśli chodzi o tworzenie typów przy pomocy interfejsu 🙂 . Nie ma w tym nic trudnego :). Oczywiście można stosować tu wszystkie te zasady, o których pisałem we wcześniejszym wpisie. Nic nie stoi na przeszkodzie, aby któreś z pól interfejsu było typu any, lub typem łączonym. Nie polecam tego, ale jest to możliwe 🙂 .
Jedna rzecz, która może być przydatna to wstawianie do interfejsów pól niewymaganych. Aby to zrobić, na końcu nazwy takiego pola należy dodać znak zapytania:
1 2 3 4 5 |
interface Dog { name: string; age: number; rabid?: boolean; } |
Teraz obiekty typu Dog muszą mieć pola name oraz age i ewentualnie mogą posiadać też pole rabid. Należy pamiętać, że jeśli pole takie faktycznie pojawi się w obiekcie, może w tym wypadku jako wartość przyjmować jedynie typy boolowskie: true lub false
Enumeratory
Enumeratory to kolejna klasyczna konstrukcja danych. W TypeScript możemy wykorzystywać ją do tworzenia nowych typów.
Z zasady enumeratory to kolekcje danych służących do przechowywania wartości o wymownych nazwach, którymi możesz zastąpić dane o nazwy nie mają jasnego znaczenia. Najczęstszym przykładem jest definiowane stanów różnych elementów programu. Powiedzmy, że mój typ Dog posiada pole o nazwie state:
1 2 3 4 5 |
interface Dog { name: string; age: number; state: number; } |
Pole state jako typ danych przyjmuje liczbę. Wiem, że liczba 1 oznacza stan wesołego psa, liczba 2 śpiącego psa a liczba 3 głodnego psa. Mogę na tej podstawie tworzyć logikę dla tego obiektu, która sprawdza w jakim stanie jest pies, i zależnie od tego uruchamia odpowiednie mechanizmy:
1 2 3 |
if(maja.state == 3) { feedDog(maja); } |
Wszystko wydaje się być w porządku, ale jeśli na kod spojrzy ktoś inny, może nie wiedzieć czym jest liczba 3 i dlaczego właśnie tam się znajduje. Z pomocą przychodzą tu enumeratory. Wystarczy że stworzę taką konstrukcję:
1 2 3 4 5 |
enum dogStates { happy = 1, sleeping, hungry, } |
Oto enumerator o nazwie dogStates. Taki zapis jak wyżej powoduje, że pole happy odpowiada liczbie jeden, sleeping liczbie dwa i tak dalej. Mogę teraz zmienić mój warunek w logice obsługi typu Dog:
1 2 3 |
if(maja.state == dogStates.hungry) { feedDog(maja); } |
Od razu wygląda to dużo lepiej. I wiadomo już co się dzieje w tej części kodu, bez konieczności szukania bo całej jego bazie.
Ale to tylko połowa udogodnienia. Można jeszcze lepiej wykorzystać dogStates, jako typ:
1 2 3 4 5 |
interface Dog { name: string; age: number; state: dogStates; } |
Teraz interfejs Dog posiada pole state, typu dogStates. A oto obiekt typu z polem którego typ powstał na podstawie enumeratora:
1 2 3 4 5 |
maja = { name: 'maja', age: 5, state: dogStates.happy, }; |
Dodatkowym bonusem jest to, że gdy po wpisaniu nazwy enumeratora, postawię kropkę, edytor pokaże mi menu kontekstowe z możliwymi wyborami.
Prawda, że fajne 🙂 .
To wszystko na dziś jeśli chodzi o tworzenie własnych typów danych. Pozostał mi do omówienia jeszcze jeden mechanizm pozwalający na dodawanie do programu własnych typów – klasy. Jemu poświęcę jednak osobny wpis. Jednak zanim to zrobię, w następnym poście muszę przedstawić jak w TypeScript działają funkcje.
Tymczasem, jak zawsze, zachęcam do polubienia mojej strony na facebooku. Na bieżąco zamieszczam tam informacje o nowościach, więc warto polubić aby nie przegapić żadnego nowego wpisu.
Wielkie dzięki za opracowywanie tych „lekcji”!
Nie ma problemu 🙂
Wspaniałe pisania. Artykuły, które mają znaczący i wnikliwe treści
internetowych są bardziej satysfakcjonujące.