Kurs JavaScript i TypeScript · Moduł 6: Podstawy TypeScript
Definicja interfejsów
W tej lekcji11
Witaj z powrotem w Parku Jurajskim! Dziś, pod czujnym okiem Dr. Henry Wu, będziemy zgłębiać fundamentalną koncepcję TypeScript, która jest równie kluczowa dla organizacji kodu, jak protokoły bezpieczeństwa są dla prawidłowego funkcjonowania parku - interfejsy.
Czym są interfejsy w TypeScript?
Interfejsy w TypeScript definiują kontrakty w kodzie oraz opisują strukturę obiektów. Są one jak szczegółowe protokoły, które określają, co powinno zawierać dany obiekt. W kontekście Parku Jurajskiego, interfejsy można porównać do standardów bezpieczeństwa i protokołów obsługi dla różnych gatunków dinozaurów - każdy gatunek ma swoje specyficzne wymagania i cechy.
Podstawowa składnia interfejsu
Interfejs definiujemy za pomocą słowa kluczowego interface:
1interface Dinozaur {
2 id: number;
3 nazwa: string;
4 gatunek: string;
5 wiek: number;
6 waga: number;
7}
8
9// Tworzymy obiekt, który musi spełniać wymagania interfejsu
10const rex: Dinozaur = {
11 id: 1,
12 nazwa: "Rex",
13 gatunek: "Tyrannosaurus",
14 wiek: 7,
15 waga: 8000
16};
17
18// Poniższy kod spowodowałby błąd, ponieważ brakuje wymaganych właściwości
19// const raptor: Dinozaur = {
20// id: 2,
21// nazwa: "Blue"
22// };Właściwości opcjonalne
Niektóre właściwości w interfejsie mogą być opcjonalne, oznaczane przez dodanie znaku zapytania (?). To jak oznaczenie opcjonalnych procedur obsługi dla poszczególnych dinozaurów.
1interface OkazDinozaura {
2 id: number;
3 nazwa: string;
4 gatunek: string;
5 tagRFID?: string; // Opcjonalny tag RFID
6 ostatnieBadanie?: Date; // Opcjonalna data ostatniego badania
7 notatki?: string; // Opcjonalne notatki badawcze
8}
9
10// Obiekt spełniający interfejs, ale bez wszystkich opcjonalnych właściwości
11const okaz1: OkazDinozaura = {
12 id: 101,
13 nazwa: "Specimen A",
14 gatunek: "Velociraptor"
15};
16
17// Obiekt z niektórymi opcjonalnymi właściwościami
18const okaz2: OkazDinozaura = {
19 id: 102,
20 nazwa: "Specimen B",
21 gatunek: "Dilophosaurus",
22 tagRFID: "DILO-102",
23 notatki: "Wykazuje agresywne zachowania w obecności nowych obiektów"
24};Tylko do odczytu (readonly)
Niektóre właściwości powinny być modyfikowane tylko przy tworzeniu obiektu. W TypeScript możemy to osiągnąć używając modyfikatora readonly. To jak procedury bezpieczeństwa, które raz ustalone, nie mogą być zmieniane podczas normalnej operacji.
1interface KodGenetyczny {
2 readonly id: string;
3 readonly sekwencja: string;
4 readonly gatunekDawca: string;
5 modyfikacje: string[];
6 dataIzolacji: Date;
7}
8
9// Tworzymy kod genetyczny
10const kodTRex: KodGenetyczny = {
11 id: "GEN-TREX-001",
12 sekwencja: "ACGTTGCAAGCTA...",
13 gatunekDawca: "Żaba zachodnioafrykańska",
14 modyfikacje: ["Uzupełnienie luk DNA", "Korekta genów agresji"],
15 dataIzolacji: new Date("2022-05-15")
16};
17
18// Możemy dodawać nowe modyfikacje
19kodTRex.modyfikacje.push("Wzmocnienie odporności immunologicznej");
20
21// Ale nie możemy zmienić wartości pól readonly
22// kodTRex.id = "GEN-TREX-002"; // Błąd: Cannot assign to 'id' because it is a read-only property
23// kodTRex.sekwencja = "GCTA..."; // Błąd: Cannot assign to 'sekwencja' because it is a read-only propertyInterfejsy funkcji
Interfejsy mogą również definiować typy funkcji, co jest przydatne przy opisywaniu funkcji zwrotnych lub przekazywanych jako parametry. W kontekście parku, to jak definiowanie standardowych procedur obsługi różnych sytuacji.
1// Interfejs funkcji bezpieczeństwa
2interface SprawdzenieBezpieczeństwa {
3 (poziomZagrożenia: number, strefaParku: string): boolean;
4}
5
6// Implementacja funkcji zgodnej z interfejsem
7const sprawdźDostęp: SprawdzenieBezpieczeństwa = function(poziom, strefa) {
8 // Logika sprawdzania dostępu
9 if (strefa === "Wybieg T-Rex" && poziom < 5) {
10 return false; // Odmowa dostępu do strefy T-Rex dla pracowników poniżej poziomu 5
11 }
12
13 if (strefa.includes("Laboratorium") && poziom < 4) {
14 return false; // Odmowa dostępu do laboratoriów dla pracowników poniżej poziomu 4
15 }
16
17 return true; // Dostęp dozwolony dla pozostałych przypadków
18};
19
20// Użycie funkcji
21console.log(sprawdźDostęp(3, "Wybieg T-Rex")); // false
22console.log(sprawdźDostęp(5, "Wybieg T-Rex")); // true
23console.log(sprawdźDostęp(3, "Obszar publiczny")); // trueIndeksowalne typy
Interfejsy mogą definiować typy indeksowalne - użyteczne, gdy mamy obiekty z dynamiczną liczbą właściwości tego samego typu. W kontekście parku, może to być baza danych dinozaurów indeksowana po ich identyfikatorach.
1// Interfejs dla bazy danych dinozaurów
2interface BazaDanychDinozaurów {
3 [id: string]: {
4 nazwa: string;
5 gatunek: string;
6 wiek: number;
7 zdrowie: "dobre" | "wymaga uwagi" | "krytyczne";
8 };
9}
10
11// Tworzenie bazy danych
12const bazaDanych: BazaDanychDinozaurów = {
13 "TREX-001": {
14 nazwa: "Rex",
15 gatunek: "Tyrannosaurus",
16 wiek: 7,
17 zdrowie: "dobre"
18 },
19 "VELOC-001": {
20 nazwa: "Blue",
21 gatunek: "Velociraptor",
22 wiek: 4,
23 zdrowie: "dobre"
24 },
25 "TRIC-003": {
26 nazwa: "Trike",
27 gatunek: "Triceratops",
28 wiek: 10,
29 zdrowie: "wymaga uwagi"
30 }
31};
32
33// Dostęp do danych
34console.log(bazaDanych["VELOC-001"].nazwa); // "Blue"
35
36// Dodawanie nowego wpisu
37bazaDanych["STEGO-002"] = {
38 nazwa: "Spike",
39 gatunek: "Stegosaurus",
40 wiek: 6,
41 zdrowie: "dobre"
42};Interfejsy klas
Interfejsy mogą również definiować kontrakty, które klasy muszą implementować. To jak standardy operacyjne, które każdy departament w parku musi spełniać.
1// Interfejs dla systemów bezpieczeństwa
2interface SystemBezpieczeństwa {
3 nazwa: string;
4 czyAktywny: boolean;
5 poziomBezpieczeństwa: number;
6
7 aktywuj(): void;
8 dezaktywuj(): void;
9 sprawdźStan(): string;
10}
11
12// Klasa implementująca interfejs
13class SystemElektronicznychOgrodzeń implements SystemBezpieczeństwa {
14 nazwa: string;
15 czyAktywny: boolean;
16 poziomBezpieczeństwa: number;
17 napięcie: number; // Dodatkowa właściwość specyficzna dla tej klasy
18
19 constructor(nazwa: string, poziomBezpieczeństwa: number, napięcie: number) {
20 this.nazwa = nazwa;
21 this.czyAktywny = false; // Początkowo nieaktywny
22 this.poziomBezpieczeństwa = poziomBezpieczeństwa;
23 this.napięcie = napięcie;
24 }
25
26 aktywuj(): void {
27 this.czyAktywny = true;
28 console.log(`System ${this.nazwa} został aktywowany. Napięcie: ${this.napięcie}V`);
29 }
30
31 dezaktywuj(): void {
32 this.czyAktywny = false;
33 console.log(`System ${this.nazwa} został dezaktywowany.`);
34 }
35
36 sprawdźStan(): string {
37 return this.czyAktywny
38 ? `${this.nazwa}: AKTYWNY (Poziom ${this.poziomBezpieczeństwa})`
39 : `${this.nazwa}: NIEAKTYWNY`;
40 }
41
42 // Metoda specyficzna dla tej klasy
43 zwiększNapięcie(o: number): void {
44 this.napięcie += o;
45 console.log(`Zwiększono napięcie do ${this.napięcie}V`);
46 }
47}
48
49// Użycie klasy
50const ogrodzenieTRex = new SystemElektronicznychOgrodzeń("Ogrodzenie T-Rex", 5, 10000);
51ogrodzenieTRex.aktywuj();
52console.log(ogrodzenieTRex.sprawdźStan());
53ogrodzenieTRex.zwiększNapięcie(2000);Rozszerzanie interfejsów
Interfejsy mogą rozszerzać inne interfejsy, podobnie jak w biologii, gdzie gatunki dzielą wspólne cechy, ale mają też swoje unikalne właściwości.
1// Podstawowy interfejs dla wszystkich zwierząt w parku
2interface Zwierzę {
3 id: number;
4 gatunek: string;
5 wiek: number;
6 waga: number;
7}
8
9// Interfejs dla dinozaurów rozszerzający interfejs Zwierzę
10interface Dinozaur extends Zwierzę {
11 era: string; // np. "Kreda", "Jura"
12 jestDrapieżnikiem: boolean;
13 dietaGłówna: string[];
14}
15
16// Interfejs dla dinozaurów drapieżnych rozszerzający interfejs Dinozaur
17interface DrapieżnyDinozaur extends Dinozaur {
18 siłaUgryzienia: number; // w PSI
19 prędkośćBiegu: number; // w km/h
20 zachowaniePodczasPolowania: string[];
21}
22
23// Tworzenie obiektu zgodnego z interfejsem DrapieżnyDinozaur
24const velociraptor: DrapieżnyDinozaur = {
25 id: 101,
26 gatunek: "Velociraptor",
27 wiek: 4,
28 waga: 70,
29 era: "Kreda",
30 jestDrapieżnikiem: true,
31 dietaGłówna: ["małe ssaki", "jaszczurki", "inne małe dinozaury"],
32 siłaUgryzienia: 1000,
33 prędkośćBiegu: 60,
34 zachowaniePodczasPolowania: ["polowanie w stadzie", "zasadzka", "pościg"]
35};Łączenie deklaracji interfejsów
W TypeScript możliwe jest definiowanie tego samego interfejsu wielokrotnie - deklaracje zostaną połączone. To przydatne, gdy chcemy dodać nowe właściwości do istniejącego interfejsu.
1// Początkowa definicja protokołu bezpieczeństwa
2interface ProtokółBezpieczeństwa {
3 nazwa: string;
4 poziomZagrożenia: number;
5 proceduraEwakuacji: string;
6}
7
8// Rozszerzenie protokołu o dodatkowe właściwości
9interface ProtokółBezpieczeństwa {
10 personelOdpowiedzialny: string[];
11 czasReakcji: number; // w minutach
12}
13
14// Obiekt musi zawierać wszystkie właściwości z obu deklaracji
15const protokółAlfa: ProtokółBezpieczeństwa = {
16 nazwa: "Protokół Alfa",
17 poziomZagrożenia: 3,
18 proceduraEwakuacji: "Ewakuacja w kierunku głównego centrum kontroli",
19 personelOdpowiedzialny: ["Dr. Wu", "Owen Grady", "Claire Dearing"],
20 czasReakcji: 15
21};Interfejsy vs typy
TypeScript oferuje również słowo kluczowe type do definiowania typów. Choć często interfejsy i typy mogą być używane zamiennie, istnieją między nimi różnice:
1// Definicja za pomocą interface
2interface PlanEwakuacji {
3 trasa: string[];
4 czasSzacunkowy: number;
5 punktyZbiórek: string[];
6}
7
8// Równoważna definicja za pomocą type
9type PlanEwakuacjiType = {
10 trasa: string[];
11 czasSzacunkowy: number;
12 punktyZbiórek: string[];
13};
14
15// Oba działają podobnie przy podstawowym użyciu
16const planA: PlanEwakuacji = {
17 trasa: ["Centrum operacyjne", "Main Street", "Hala ewakuacyjna"],
18 czasSzacunkowy: 25,
19 punktyZbiórek: ["Centrum operacyjne", "Hala ewakuacyjna"]
20};
21
22const planB: PlanEwakuacjiType = {
23 trasa: ["Wybieg Gallimimus", "Innovation Center", "Hala ewakuacyjna"],
24 czasSzacunkowy: 30,
25 punktyZbiórek: ["Innovation Center", "Hala ewakuacyjna"]
26};Główne różnice między interface a type:
Rozszerzalność:
- Interfejsy można rozszerzać przez wielokrotne deklaracje o tej samej nazwie
- Typy nie mogą być "otwarte" - raz zadeklarowany typ nie może być rozszerzony
Złożone typy:
- Typy mogą reprezentować bezpośrednio typy proste, unie, przecięcia
- Interfejsy są bardziej ukierunkowane na definiowanie kształtu obiektów
1// Możliwe tylko z typami
2type IDDinozaura = string | number;
3type KoordynatyWybiegu = [number, number, string]; // Tuple
4type StatusDinozaura = "aktywny" | "odpoczywający" | "śpiący"; // Union of literals
5
6// Bardziej naturalne z interfejsami
7interface Procedura {
8 nazwa: string;
9 kroki: string[];
10 czasWykonania: number;
11 personelOdpowiedzialny: string[];
12}Praktyczne zastosowanie interfejsów
Przyjrzyjmy się, jak możemy wykorzystać interfejsy do modelowania systemu zarządzania Parkiem Jurajskim:
1// Podstawowe interfejsy
2interface OsobaWParku {
3 id: string;
4 imię: string;
5 nazwisko: string;
6 poziomDostępu: 1 | 2 | 3 | 4 | 5;
7 dataZatrudnienia: Date;
8}
9
10interface Lokalizacja {
11 id: string;
12 nazwa: string;
13 typ: "wybieg" | "laboratorium" | "centrum operacyjne" | "strefa publiczna";
14 współrzędne: [number, number]; // [szerokość, długość]
15 wymaganyPoziomDostępu: 1 | 2 | 3 | 4 | 5;
16}
17
18interface GatunekDinozaura {
19 nazwa: string;
20 eraGeologiczna: string;
21 typ: "roślinożerca" | "drapieżnik" | "wszystkożerca";
22 przeciętnaWaga: number;
23 przeciętnyWiek: number;
24 poziomNiebezpieczeństwa: 1 | 2 | 3 | 4 | 5;
25 wymaganiaDiety: string[];
26 wymaganiaHabitatu: string[];
27}
28
29interface OkazDinozaura {
30 id: string;
31 gatunek: string;
32 imię?: string;
33 wiek: number;
34 waga: number;
35 płeć: "samiec" | "samica";
36 lokalizacja: string; // ID lokalizacji
37 stanZdrowia: "doskonały" | "dobry" | "wymaga uwagi" | "chory" | "krytyczny";
38 ostatnieBadanie: Date;
39 tagRFID: string;
40 notatki?: string;
41}
42
43// Interfejsy dla systemów zarządzania
44interface SystemŚledzeniaOkazów {
45 pobierzPozycję(idOkazu: string): [number, number];
46 śledźRuch(idOkazu: string, czasŚledzenia: number): [number, number][];
47 sprawdźCzyWStrefie(idOkazu: string, idStrefy: string): boolean;
48 alertGdyPozaStrefą(idOkazu: string, callback: (okaz: OkazDinozaura) => void): void;
49}
50
51interface SystemZarządzaniaPracownikami {
52 dodajPracownika(pracownik: OsobaWParku): void;
53 usuńPracownika(id: string): boolean;
54 przyznajDostęp(idPracownika: string, poziom: 1 | 2 | 3 | 4 | 5): boolean;
55 sprawdźObecnośćWStrefie(idPracownika: string, idStrefy: string): boolean;
56}
57
58// Przykład implementacji
59class SystemZarządzaniaParkiem {
60 private pracownicy: OsobaWParku[] = [];
61 private lokalizacje: Lokalizacja[] = [];
62 private gatunki: GatunekDinozaura[] = [];
63 private okazy: OkazDinozaura[] = [];
64
65 // Metody dla pracowników
66 dodajPracownika(pracownik: OsobaWParku): void {
67 this.pracownicy.push(pracownik);
68 console.log(`Dodano pracownika: ${pracownik.imię} ${pracownik.nazwisko}`);
69 }
70
71 // Metody dla lokalizacji
72 dodajLokalizację(lokalizacja: Lokalizacja): void {
73 this.lokalizacje.push(lokalizacja);
74 console.log(`Dodano lokalizację: ${lokalizacja.nazwa}`);
75 }
76
77 // Metody dla gatunków
78 dodajGatunek(gatunek: GatunekDinozaura): void {
79 this.gatunki.push(gatunek);
80 console.log(`Dodano gatunek: ${gatunek.nazwa}`);
81 }
82
83 // Metody dla okazów
84 dodajOkaz(okaz: OkazDinozaura): void {
85 this.okazy.push(okaz);
86 console.log(`Dodano okaz: ${okaz.id} (${okaz.gatunek})`);
87 }
88
89 // Metody zarządzania
90 sprawdźDostęp(idPracownika: string, idLokalizacji: string): boolean {
91 const pracownik = this.pracownicy.find(p => p.id === idPracownika);
92 const lokalizacja = this.lokalizacje.find(l => l.id === idLokalizacji);
93
94 if (!pracownik || !lokalizacja) return false;
95
96 return pracownik.poziomDostępu >= lokalizacja.wymaganyPoziomDostępu;
97 }
98
99 znajdźOkazyGatunku(nazwaGatunku: string): OkazDinozaura[] {
100 return this.okazy.filter(o => o.gatunek === nazwaGatunku);
101 }
102
103 znajdźOkazyWStrefie(idStrefy: string): OkazDinozaura[] {
104 return this.okazy.filter(o => o.lokalizacja === idStrefy);
105 }
106
107 generujRaportStanuParku(): {
108 liczbaPracowników: number;
109 liczbaLokalizacji: number;
110 liczbaGatunków: number;
111 liczbaOkazów: number;
112 stanZdrowiaOkazów: {
113 doskonały: number;
114 dobry: number;
115 wymagaUwagi: number;
116 chory: number;
117 krytyczny: number;
118 }
119 } {
120 // Analiza stanu zdrowia wszystkich okazów
121 const stanZdrowia = {
122 doskonały: 0,
123 dobry: 0,
124 wymagaUwagi: 0,
125 chory: 0,
126 krytyczny: 0
127 };
128
129 this.okazy.forEach(okaz => {
130 if (okaz.stanZdrowia === "doskonały") stanZdrowia.doskonały++;
131 else if (okaz.stanZdrowia === "dobry") stanZdrowia.dobry++;
132 else if (okaz.stanZdrowia === "wymaga uwagi") stanZdrowia.wymagaUwagi++;
133 else if (okaz.stanZdrowia === "chory") stanZdrowia.chory++;
134 else if (okaz.stanZdrowia === "krytyczny") stanZdrowia.krytyczny++;
135 });
136
137 return {
138 liczbaPracowników: this.pracownicy.length,
139 liczbaLokalizacji: this.lokalizacje.length,
140 liczbaGatunków: this.gatunki.length,
141 liczbaOkazów: this.okazy.length,
142 stanZdrowiaOkazów: stanZdrowia
143 };
144 }
145}
146
147// Przykład użycia
148const parkJurajski = new SystemZarządzaniaParkiem();
149
150// Dodanie lokalizacji
151parkJurajski.dodajLokalizację({
152 id: "LOC-001",
153 nazwa: "Wybieg T-Rex",
154 typ: "wybieg",
155 współrzędne: [23.45, -67.89],
156 wymaganyPoziomDostępu: 4
157});
158
159// Dodanie gatunku
160parkJurajski.dodajGatunek({
161 nazwa: "Tyrannosaurus Rex",
162 eraGeologiczna: "Kreda",
163 typ: "drapieżnik",
164 przeciętnaWaga: 7000,
165 przeciętnyWiek: 30,
166 poziomNiebezpieczeństwa: 5,
167 wymaganiaDiety: ["duże ilości mięsa", "uzupełnianie wapnia"],
168 wymaganiaHabitatu: ["duży obszar", "naturalna roślinność", "dostęp do wody"]
169});
170
171// Dodanie pracownika
172parkJurajski.dodajPracownika({
173 id: "EMP-001",
174 imię: "Owen",
175 nazwisko: "Grady",
176 poziomDostępu: 5,
177 dataZatrudnienia: new Date("2010-06-15")
178});
179
180// Dodanie okazu
181parkJurajski.dodajOkaz({
182 id: "DINO-001",
183 gatunek: "Tyrannosaurus Rex",
184 imię: "Rexy",
185 wiek: 7,
186 waga: 7500,
187 płeć: "samica",
188 lokalizacja: "LOC-001",
189 stanZdrowia: "dobry",
190 ostatnieBadanie: new Date("2023-03-20"),
191 tagRFID: "RFID-TREX-001"
192});
193
194// Generowanie raportu
195const raport = parkJurajski.generujRaportStanuParku();
196console.log("Raport stanu Parku Jurajskiego:");
197console.log(`Liczba pracowników: ${raport.liczbaPracowników}`);
198console.log(`Liczba lokalizacji: ${raport.liczbaLokalizacji}`);
199console.log(`Liczba gatunków: ${raport.liczbaGatunków}`);
200console.log(`Liczba okazów: ${raport.liczbaOkazów}`);
201console.log("Stan zdrowia okazów:", raport.stanZdrowiaOkazów);Podsumowanie
Interfejsy w TypeScript są potężnym narzędziem do modelowania struktur danych i definiowania kontraktów w kodzie. W kontekście zarządzania Parkiem Jurajskim, pozwalają nam precyzyjnie określić, jak powinny wyglądać różne systemy, procedury i elementy parku.
| Cecha interfejsu | Zastosowanie w Parku Jurajskim |
|---|---|
| Podstawowa definicja | Struktury danych dla dinozaurów, pracowników, lokalizacji |
| Właściwości opcjonalne | Dodatkowe informacje, które mogą, ale nie muszą być dostępne |
| Właściwości readonly | Dane, które nie powinny być zmieniane, np. ID genetyczne |
| Interfejsy funkcji | Definicje procedur bezpieczeństwa i protokołów |
| Typy indeksowalne | Dynamiczne bazy danych dinozaurów i zasobów |
| Interfejsy klas | Standardy dla systemów bezpieczeństwa i monitoringu |
| Rozszerzanie interfejsów | Dziedziczenie cech między gatunkami dinozaurów |
| Łączenie deklaracji | Rozszerzanie protokołów o dodatkowe procedury |
Dr. Wu podsumowuje: "Interfejsy w TypeScript, podobnie jak nasze protokoły w parku, zapewniają, że każdy element systemu działa zgodnie z oczekiwaniami. Dzięki nim możemy budować złożone systemy z pewnością, że poszczególne komponenty będą ze sobą współpracować - co jest absolutnie kluczowe, gdy mamy do czynienia z gatunkami, które mogą być niebezpieczne, jeśli coś pójdzie nie tak."
Kod do tej lekcji: index.ts
1// Interfejsy w Parku Jurajskim
2console.log("Interfejsy TypeScript\n");
3
4// ===========================================
5// 1. Podstawowe interfejsy
6// ===========================================
7console.log("=== PODSTAWOWE INTERFEJSY ===");
8
9// Prosty interfejs
10interface Dinosaur {
11 name: string;
12 species: string;
13 age: number;
14 weight: number;
15}
16
17const trex: Dinosaur = {
18 name: "Rexy",
19 species: "Tyrannosaurus Rex",
20 age: 5,
21 weight: 8000
22};
23
24console.log("T-Rex:", trex);
25
26// ===========================================
27// 2. Opcjonalne właściwości
28// ===========================================
29console.log("\n=== OPCJONALNE WŁAŚCIWOŚCI ===");
30
31interface DinosaurWithOptionals {
32 name: string;
33 species: string;
34 age: number;
35 nickname?: string; // opcjonalne
36 medicalNotes?: string; // opcjonalne
37}
38
39const blue: DinosaurWithOptionals = {
40 name: "Blue",
41 species: "Velociraptor",
42 age: 3,
43 nickname: "The Beta"
44 // medicalNotes pominięte - OK
45};
46
47console.log("Blue:", blue);
48console.log("Has medical notes:", blue.medicalNotes !== undefined);
49
50// ===========================================
51// 3. Readonly properties
52// ===========================================
53console.log("\n=== READONLY PROPERTIES ===");
54
55interface DinosaurWithReadonly {
56 readonly id: string; // nie można zmieniać
57 readonly dnaSequence: string; // nie można zmieniać
58 name: string; // można zmieniać
59 age: number; // można zmieniać
60}
61
62const dino: DinosaurWithReadonly = {
63 id: "DINO-001",
64 dnaSequence: "ACGT...",
65 name: "Rex",
66 age: 5
67};
68
69// dino.id = "DINO-002"; // Error: Cannot assign to 'id'
70dino.name = "Rexy"; // OK
71dino.age = 6; // OK
72
73console.log("Dinosaur:", dino);
74
75// ===========================================
76// 4. Metody w interfejsach
77// ===========================================
78console.log("\n=== METODY W INTERFEJSACH ===");
79
80interface DinosaurWithMethods {
81 name: string;
82 species: string;
83 energy: number;
84
85 // Metody
86 roar(): string;
87 eat(amount: number): void;
88 sleep(hours: number): void;
89 getStatus(): string;
90}
91
92class TyrannosaurusRex implements DinosaurWithMethods {
93 name: string;
94 species: string;
95 energy: number;
96
97 constructor(name: string) {
98 this.name = name;
99 this.species = "Tyrannosaurus Rex";
100 this.energy = 100;
101 }
102
103 roar(): string {
104 this.energy -= 5;
105 return `${this.name} roars: ROAAAAAR!`;
106 }
107
108 eat(amount: number): void {
109 this.energy += amount * 2;
110 if (this.energy > 100) this.energy = 100;
111 console.log(`${this.name} ate ${amount}kg. Energy: ${this.energy}`);
112 }
113
114 sleep(hours: number): void {
115 this.energy += hours * 10;
116 if (this.energy > 100) this.energy = 100;
117 console.log(`${this.name} slept ${hours}h. Energy: ${this.energy}`);
118 }
119
120 getStatus(): string {
121 if (this.energy > 70) return "Energetic";
122 if (this.energy > 30) return "Normal";
123 return "Tired";
124 }
125}
126
127const rexy = new TyrannosaurusRex("Rexy");
128console.log(rexy.roar());
129rexy.eat(50);
130console.log("Status:", rexy.getStatus());
131
132// ===========================================
133// 5. Index signatures
134// ===========================================
135console.log("\n=== INDEX SIGNATURES ===");
136
137// Dynamiczne klucze
138interface DinosaurCollection {
139 [id: string]: Dinosaur;
140}
141
142const collection: DinosaurCollection = {
143 "T-001": trex,
144 "V-001": blue
145};
146
147console.log("Collection:", collection);
148console.log("T-001:", collection["T-001"]);
149
150// Mieszane właściwości
151interface PaddockData {
152 id: string;
153 capacity: number;
154 [key: string]: any; // inne dynamiczne właściwości
155}
156
157const paddock: PaddockData = {
158 id: "P-01",
159 capacity: 10,
160 securityLevel: "high",
161 temperature: 25,
162 humidity: 60
163};
164
165console.log("Paddock:", paddock);
166
167// ===========================================
168// 6. Funkcje w interfejsach
169// ===========================================
170console.log("\n=== FUNKCJE W INTERFEJSACH ===");
171
172// Typ funkcji
173interface DinosaurFilter {
174 (dino: Dinosaur): boolean;
175}
176
177const carnivoreFilter: DinosaurFilter = (dino) => {
178 return dino.species.includes("saurus") && dino.weight > 1000;
179};
180
181console.log("Is T-Rex carnivore:", carnivoreFilter(trex));
182
183// Interface dla funkcji z właściwościami
184interface DinosaurValidator {
185 (dino: Dinosaur): boolean;
186 errorMessage: string;
187}
188
189const ageValidator: DinosaurValidator = (dino) => {
190 return dino.age >= 0 && dino.age <= 100;
191};
192ageValidator.errorMessage = "Age must be between 0 and 100";
193
194console.log("Validator:", ageValidator(trex));
195console.log("Error message:", ageValidator.errorMessage);
196
197// ===========================================
198// 7. Zagnieżdżone interfejsy
199// ===========================================
200console.log("\n=== ZAGNIEŻDŻONE INTERFEJSY ===");
201
202interface Location {
203 latitude: number;
204 longitude: number;
205 altitude: number;
206}
207
208interface Paddock {
209 id: string;
210 name: string;
211 location: Location; // zagnieżdżony interfejs
212 dimensions: {
213 width: number;
214 length: number;
215 height: number;
216 };
217 dinosaurs: Dinosaur[];
218}
219
220const trexPaddock: Paddock = {
221 id: "P-01",
222 name: "T-Rex Territory",
223 location: {
224 latitude: 22.3964,
225 longitude: -159.5261,
226 altitude: 50
227 },
228 dimensions: {
229 width: 1000,
230 length: 1500,
231 height: 50
232 },
233 dinosaurs: [trex]
234};
235
236console.log("Paddock:", trexPaddock);
237console.log("Location:", trexPaddock.location);
238
239// ===========================================
240// 8. Hybrid types
241// ===========================================
242console.log("\n=== HYBRID TYPES ===");
243
244interface Counter {
245 (start: number): string;
246 interval: number;
247 reset(): void;
248}
249
250function createCounter(): Counter {
251 let count = 0;
252
253 const counter = function(start: number) {
254 count = start;
255 return `Count: ${count}`;
256 } as Counter;
257
258 counter.interval = 1;
259 counter.reset = function() {
260 count = 0;
261 };
262
263 return counter;
264}
265
266const myCounter = createCounter();
267console.log(myCounter(5));
268console.log("Interval:", myCounter.interval);
269myCounter.reset();
270
271// ===========================================
272// 9. Podsumowanie
273// ===========================================
274console.log("\n=== PODSUMOWANIE ===");
275console.log("Podstawowe interfejsy - struktura obiektów");
276console.log("Opcjonalne właściwości - ?");
277console.log("Readonly - niezmienne właściwości");
278console.log("Metody - funkcje w interfejsach");
279console.log("Index signatures - dynamiczne klucze");
280console.log("Zagnieżdżone interfejsy - złożone struktury");
281
282console.log("\nInterfejsy opanowane!");Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Czym interfejs różni się od type alias w TypeScript?
2. Jak oznaczyć właściwość interfejsu jako opcjonalną w TypeScript?
Zadania praktyczne w grze
- Edytor kodu
Zdefiniuj interfejs Dinosaur z polami name: string, species: string, weight?: number i metodą roar(): void.
- Układanie w poziomie
Ułóż elementy definicji interfejsu TypeScript:
- Klikanie w kolejności
Ułóż elementy definicji pola readonly w interfejsie:
- Układanie w pionie
Uporządkuj koncepty interfejsów od najprostszych do najbardziej zaawansowanych: