Kurs JavaScript i TypeScript · Moduł 6: Podstawy TypeScript
Rozszerzanie interfejsów
W tej lekcji9
Witaj ponownie w Parku Jurajskim! Dziś Dr. Henry Wu poprowadzi nas przez zaawansowaną technikę TypeScript, która jest podstawą tworzenia skalowalnych i elastycznych systemów - rozszerzanie interfejsów. Podobnie jak naukowcy w parku rozszerzają podstawowe cechy dinozaurów o nowe właściwości genetyczne, tak w TypeScript możemy rozszerzać podstawowe interfejsy o nowe funkcjonalności.
Podstawowy mechanizm rozszerzania interfejsów
Rozszerzanie interfejsów w TypeScript pozwala nam tworzyć nowe interfejsy na bazie istniejących, dodając do nich nowe właściwości lub metody. Używamy do tego słowa kluczowego extends:
1// Bazowy interfejs dla wszystkich stworzeń w parku
2interface Stworzenie {
3 id: string;
4 nazwa: string;
5 gatunek: string;
6 wiek: number;
7 waga: number;
8}
9
10// Rozszerzenie bazowego interfejsu o dodatkowe właściwości dla dinozaurów
11interface Dinozaur extends Stworzenie {
12 eraPochodzenia: string; // np. "Jura", "Kreda"
13 dieta: "roślinożerca" | "mięsożerca" | "wszystkożerca";
14 niebezpieczny: boolean;
15}
16
17// Teraz interfejs Dinozaur zawiera wszystkie pola z Stworzenie plus swoje własne
18const trex: Dinozaur = {
19 id: "TREX-01",
20 nazwa: "Rex",
21 gatunek: "Tyrannosaurus Rex",
22 wiek: 7,
23 waga: 7000,
24 eraPochodzenia: "Kreda",
25 dieta: "mięsożerca",
26 niebezpieczny: true
27};Rozszerzanie wielu interfejsów
Interfejs może rozszerzać wiele innych interfejsów jednocześnie, co pozwala na komponowanie złożonych typów z mniejszych, wyspecjalizowanych części - podobnie jak naukowcy łączą DNA różnych gatunków, aby stworzyć hybrydy:
1// Interfejs definiujący zdolności drapieżnika
2interface ZdolnościDrapieżnika {
3 siłaUgryzienia: number; // w PSI
4 prędkośćBiegu: number; // w km/h
5 stylPolowania: "zasadzka" | "pościg" | "stadny";
6}
7
8// Interfejs definiujący cechy zwierzęcia wodnego
9interface ZwierzęWodne {
10 zdolnośćPływania: number; // od 1 do 10
11 maksGłębokość: number; // w metrach
12 odpornośćNaSłonąWodę: boolean;
13}
14
15// Interfejs dla morskiego drapieżnika łączący oba powyższe interfejsy
16interface MorskiDrapieżnik extends ZdolnościDrapieżnika, ZwierzęWodne {
17 nazwaNaukowa: string;
18}
19
20// Tworzenie dinozaura, który jest morskim drapieżnikiem
21const mosasaurus: MorskiDrapieżnik & Dinozaur = {
22 // Właściwości z Stworzenie
23 id: "MOSA-01",
24 nazwa: "Mosa",
25 gatunek: "Mosasaurus",
26 wiek: 15,
27 waga: 15000,
28
29 // Właściwości z Dinozaur
30 eraPochodzenia: "Kreda",
31 dieta: "mięsożerca",
32 niebezpieczny: true,
33
34 // Właściwości z ZdolnościDrapieżnika
35 siłaUgryzienia: 18000,
36 prędkośćBiegu: 0, // nie biega, pływa
37 stylPolowania: "zasadzka",
38
39 // Właściwości z ZwierzęWodne
40 zdolnośćPływania: 9,
41 maksGłębokość: 500,
42 odpornośćNaSłonąWodę: true,
43
44 // Właściwości z MorskiDrapieżnik
45 nazwaNaukowa: "Mosasaurus hoffmannii"
46};
47
48// Demonstracja dostępu do różnych właściwości
49function analizujMorskiegoDrapieżnika(dino: MorskiDrapieżnik & Dinozaur): void {
50 console.log(`Analizuję: ${dino.nazwa} (${dino.gatunek})`);
51 console.log(`Siła ugryzienia: ${dino.siłaUgryzienia} PSI`);
52 console.log(`Może nurkować do głębokości: ${dino.maksGłębokość} metrów`);
53 console.log(`Zagrożenie dla zwiedzających: ${dino.niebezpieczny ? "WYSOKIE" : "NISKIE"}`);
54}
55
56analizujMorskiegoDrapieżnika(mosasaurus);Rozszerzanie interfejsów a dziedziczenie klas
Rozszerzanie interfejsów jest podobne do dziedziczenia klas, ale ma kilka kluczowych różnic. W Parku Jurajskim, można to porównać do różnicy między "opisem idealnego dinozaura" (interfejs) a "rzeczywistym dinozaurem" (klasa):
1// Interfejs dla systemów bezpieczeństwa
2interface SystemBezpieczeństwa {
3 nazwa: string;
4 poziomBezpieczeństwa: number;
5 czyAktywny: boolean;
6
7 aktywuj(): void;
8 dezaktywuj(): void;
9 testuj(): boolean;
10}
11
12// Klasa implementująca interfejs
13class ElektroniczneOgrodzenie implements SystemBezpieczeństwa {
14 nazwa: string;
15 poziomBezpieczeństwa: number;
16 czyAktywny: boolean;
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.poziomBezpieczeństwa = poziomBezpieczeństwa;
22 this.czyAktywny = false;
23 this.napięcie = napięcie;
24 }
25
26 aktywuj(): void {
27 this.czyAktywny = true;
28 console.log(`System ${this.nazwa} został aktywowany z napięciem ${this.napięcie}V`);
29 }
30
31 dezaktywuj(): void {
32 this.czyAktywny = false;
33 console.log(`System ${this.nazwa} został dezaktywowany`);
34 }
35
36 testuj(): boolean {
37 console.log(`Testowanie systemu ${this.nazwa}...`);
38 return Math.random() > 0.1; // 90% szans na pomyślny test
39 }
40
41 // Metoda specyficzna dla tej klasy
42 zwiększNapięcie(o: number): void {
43 this.napięcie += o;
44 console.log(`Napięcie zwiększone do ${this.napięcie}V`);
45 }
46}
47
48// Rozszerzona wersja SystemBezpieczeństwa dla bardziej zaawansowanych systemów
49interface ZaawansowanySystemBezpieczeństwa extends SystemBezpieczeństwa {
50 backupZasilanie: boolean;
51 monitoringWideo: boolean;
52 powiadomieniaAlarmowe: boolean;
53
54 uruchomProcedurębezpieczeństwa(poziomZagrożenia: number): void;
55}
56
57// Klasa implementująca rozszerzony interfejs
58class ZintegrowanySstemBezpieczeństwa implements ZaawansowanySystemBezpieczeństwa {
59 nazwa: string;
60 poziomBezpieczeństwa: number;
61 czyAktywny: boolean;
62 backupZasilanie: boolean;
63 monitoringWideo: boolean;
64 powiadomieniaAlarmowe: boolean;
65
66 constructor(nazwa: string, poziomBezpieczeństwa: number) {
67 this.nazwa = nazwa;
68 this.poziomBezpieczeństwa = poziomBezpieczeństwa;
69 this.czyAktywny = false;
70 this.backupZasilanie = true;
71 this.monitoringWideo = true;
72 this.powiadomieniaAlarmowe = true;
73 }
74
75 aktywuj(): void {
76 this.czyAktywny = true;
77 console.log(`Zintegrowany system ${this.nazwa} został aktywowany`);
78 }
79
80 dezaktywuj(): void {
81 this.czyAktywny = false;
82 console.log(`Zintegrowany system ${this.nazwa} został dezaktywowany`);
83 }
84
85 testuj(): boolean {
86 console.log(`Testowanie zintegrowanego systemu ${this.nazwa}...`);
87 const testZasilania = Math.random() > 0.05;
88 const testMonitoringu = Math.random() > 0.05;
89 const testPowiadomień = Math.random() > 0.05;
90
91 return testZasilania && testMonitoringu && testPowiadomień;
92 }
93
94 uruchomProcedurębezpieczeństwa(poziomZagrożenia: number): void {
95 console.log(`Uruchamianie procedury bezpieczeństwa poziomu ${poziomZagrożenia} dla systemu ${this.nazwa}`);
96 this.aktywuj();
97
98 if (poziomZagrożenia >= 4) {
99 console.log("UWAGA: Wysoki poziom zagrożenia! Uruchamianie wszystkich zabezpieczeń!");
100 // Tutaj dodatkowe działania dla wysokiego poziomu zagrożenia
101 }
102 }
103}
104
105// Demonstracja użycia
106const ogrodzenieTRex = new ElektroniczneOgrodzenie("Wybieg T-Rex", 5, 10000);
107ogrodzenieTRex.aktywuj();
108ogrodzenieTRex.zwiększNapięcie(2000);
109
110const zintegrowanySstemPark = new ZintegrowanySstemBezpieczeństwa("System Główny", 5);
111zintegrowanySstemPark.uruchomProcedurębezpieczeństwa(4);Rozszerzanie i implementacja generycznych interfejsów
Generyczne interfejsy są szczególnie potężne przy modelowaniu złożonych systemów, takich jak monitoring różnych typów danych w parku:
1// Generyczny interfejs dla systemu monitoringu
2interface SystemMonitoringu<T> {
3 identyfikator: string;
4 typDanych: string;
5 aktualneDane: T;
6 historyczneDane: T[];
7
8 zapisz(dane: T): void;
9 pobierzOstatnie(n: number): T[];
10 analizujTrend(): string;
11}
12
13// Przykładowy typ danych dla monitoringu warunków środowiskowych
14interface DaneŚrodowiskowe {
15 temperatura: number;
16 wilgotność: number;
17 poziomCO2: number;
18 nasłonecznienie: number;
19 ciśnienie: number;
20 czasPomiaru: Date;
21}
22
23// Implementacja systemu monitoringu dla konkretnego typu danych
24class MonitoringŚrodowiskowy implements SystemMonitoringu<DaneŚrodowiskowe> {
25 identyfikator: string;
26 typDanych: string;
27 aktualneDane: DaneŚrodowiskowe;
28 historyczneDane: DaneŚrodowiskowe[];
29
30 constructor(identyfikator: string) {
31 this.identyfikator = identyfikator;
32 this.typDanych = "środowiskowe";
33 this.aktualneDane = {
34 temperatura: 0,
35 wilgotność: 0,
36 poziomCO2: 0,
37 nasłonecznienie: 0,
38 ciśnienie: 0,
39 czasPomiaru: new Date()
40 };
41 this.historyczneDane = [];
42 }
43
44 zapisz(dane: DaneŚrodowiskowe): void {
45 this.aktualneDane = dane;
46 this.historyczneDane.push(dane);
47 console.log(`Zapisano nowe dane dla monitora ${this.identyfikator} o czasie ${dane.czasPomiaru.toISOString()}`);
48 }
49
50 pobierzOstatnie(n: number): DaneŚrodowiskowe[] {
51 return this.historyczneDane.slice(-n);
52 }
53
54 analizujTrend(): string {
55 if (this.historyczneDane.length < 2) {
56 return "Za mało danych do analizy trendu";
57 }
58
59 const ostatnie = this.historyczneDane[this.historyczneDane.length - 1];
60 const przedostatnie = this.historyczneDane[this.historyczneDane.length - 2];
61
62 let trendy = [];
63
64 if (ostatnie.temperatura > przedostatnie.temperatura) {
65 trendy.push("Temperatura: WZROST");
66 } else if (ostatnie.temperatura < przedostatnie.temperatura) {
67 trendy.push("Temperatura: SPADEK");
68 } else {
69 trendy.push("Temperatura: STABILNA");
70 }
71
72 if (ostatnie.wilgotność > przedostatnie.wilgotność) {
73 trendy.push("Wilgotność: WZROST");
74 } else if (ostatnie.wilgotność < przedostatnie.wilgotność) {
75 trendy.push("Wilgotność: SPADEK");
76 } else {
77 trendy.push("Wilgotność: STABILNA");
78 }
79
80 // Podobne analizy dla pozostałych parametrów
81
82 return trendy.join(", ");
83 }
84
85 // Metoda specyficzna dla monitoringu środowiskowego
86 ostrzeżenieTemperaturowe(): boolean {
87 return this.aktualneDane.temperatura > 35 || this.aktualneDane.temperatura < 15;
88 }
89}
90
91// Rozszerzenie bazowego interfejsu o specyficzne funkcje dla monitoringu dinozaurów
92interface SystemMonitoringuDinozaurów<T> extends SystemMonitoringu<T> {
93 idDinozaura: string;
94 gatunek: string;
95 progiAlarmowe: Partial<T>;
96
97 sprawdźProgiAlarmowe(): boolean;
98 wyslijAlertJeśliPotrzebny(): void;
99}
100
101// Typ danych dla monitoringu zdrowia dinozaurów
102interface DaneZdrowiaDinozaura {
103 temperaturaCiała: number;
104 tętno: number;
105 ciśnienieKrwi: [number, number]; // [skurczowe, rozkurczowe]
106 poziomStresu: number; // 1-10
107 poziomAktywności: number; // 1-10
108 czasPomiaru: Date;
109}
110
111// Implementacja specyficznego systemu monitoringu dla dinozaurów
112class MonitoringZdrowiaDinozaura implements SystemMonitoringuDinozaurów<DaneZdrowiaDinozaura> {
113 identyfikator: string;
114 typDanych: string;
115 aktualneDane: DaneZdrowiaDinozaura;
116 historyczneDane: DaneZdrowiaDinozaura[];
117 idDinozaura: string;
118 gatunek: string;
119 progiAlarmowe: Partial<DaneZdrowiaDinozaura>;
120
121 constructor(identyfikator: string, idDinozaura: string, gatunek: string) {
122 this.identyfikator = identyfikator;
123 this.typDanych = "zdrowia";
124 this.aktualneDane = {
125 temperaturaCiała: 0,
126 tętno: 0,
127 ciśnienieKrwi: [0, 0],
128 poziomStresu: 0,
129 poziomAktywności: 0,
130 czasPomiaru: new Date()
131 };
132 this.historyczneDane = [];
133 this.idDinozaura = idDinozaura;
134 this.gatunek = gatunek;
135
136 // Ustawienie progów alarmowych w zależności od gatunku
137 if (gatunek === "Tyrannosaurus") {
138 this.progiAlarmowe = {
139 temperaturaCiała: 39, // Powyżej tej temperatury - alarm
140 tętno: 95, // Powyżej tego tętna - alarm
141 poziomStresu: 7 // Powyżej tego poziomu stresu - alarm
142 };
143 } else if (gatunek === "Velociraptor") {
144 this.progiAlarmowe = {
145 temperaturaCiała: 40,
146 tętno: 120,
147 poziomStresu: 8
148 };
149 } else {
150 // Domyślne progi dla innych gatunków
151 this.progiAlarmowe = {
152 temperaturaCiała: 38,
153 tętno: 100,
154 poziomStresu: 7
155 };
156 }
157 }
158
159 zapisz(dane: DaneZdrowiaDinozaura): void {
160 this.aktualneDane = dane;
161 this.historyczneDane.push(dane);
162 console.log(`Zapisano nowe dane zdrowia dla dinozaura ${this.idDinozaura} (${this.gatunek})`);
163
164 // Sprawdzamy czy wartości nie przekraczają progów
165 this.wyslijAlertJeśliPotrzebny();
166 }
167
168 pobierzOstatnie(n: number): DaneZdrowiaDinozaura[] {
169 return this.historyczneDane.slice(-n);
170 }
171
172 analizujTrend(): string {
173 if (this.historyczneDane.length < 2) {
174 return "Za mało danych do analizy trendu";
175 }
176
177 // Podobna analiza trendu jak w klasie MonitoringŚrodowiskowy
178 //
179 return "Analiza trendu zdrowia (przykładowa implementacja)";
180 }
181
182 sprawdźProgiAlarmowe(): boolean {
183 let przekroczonyPróg = false;
184
185 if (this.progiAlarmowe.temperaturaCiała &&
186 this.aktualneDane.temperaturaCiała > this.progiAlarmowe.temperaturaCiała) {
187 console.log(`ALARM: Temperatura ciała dinozaura ${this.idDinozaura} przekracza próg!`);
188 przekroczonyPróg = true;
189 }
190
191 if (this.progiAlarmowe.tętno &&
192 this.aktualneDane.tętno > this.progiAlarmowe.tętno) {
193 console.log(`ALARM: Tętno dinozaura ${this.idDinozaura} przekracza próg!`);
194 przekroczonyPróg = true;
195 }
196
197 if (this.progiAlarmowe.poziomStresu &&
198 this.aktualneDane.poziomStresu > this.progiAlarmowe.poziomStresu) {
199 console.log(`ALARM: Poziom stresu dinozaura ${this.idDinozaura} przekracza próg!`);
200 przekroczonyPróg = true;
201 }
202
203 return przekroczonyPróg;
204 }
205
206 wyslijAlertJeśliPotrzebny(): void {
207 if (this.sprawdźProgiAlarmowe()) {
208 console.log(`Wysyłanie alertu do zespołu weterynaryjnego dla dinozaura ${this.idDinozaura}!`);
209 // W rzeczywistym systemie wysłalibyśmy tu powiadomienie
210 }
211 }
212
213 // Metoda specyficzna dla monitoringu zdrowia dinozaurów
214 rekomendujInterwencję(): string {
215 let rekomendacje = [];
216
217 if (this.aktualneDane.temperaturaCiała > this.progiAlarmowe.temperaturaCiała!) {
218 rekomendacje.push("Obniżenie temperatury ciała");
219 }
220
221 if (this.aktualneDane.poziomStresu > this.progiAlarmowe.poziomStresu!) {
222 rekomendacje.push("Podanie środków uspokajających");
223 }
224
225 if (this.aktualneDane.poziomAktywności < 3) {
226 rekomendacje.push("Sprawdzenie poziomu energii i ewentualne zwiększenie dawki pokarmowej");
227 }
228
229 return rekomendacje.length > 0
230 ? `Rekomendowane działania: ${rekomendacje.join(", ")}`
231 : "Brak rekomendacji - parametry w normie";
232 }
233}
234
235// Przykład użycia
236const monitorPaddockTRex = new MonitoringŚrodowiskowy("ENV-TREX-01");
237monitorPaddockTRex.zapisz({
238 temperatura: 28.5,
239 wilgotność: 75,
240 poziomCO2: 420,
241 nasłonecznienie: 80,
242 ciśnienie: 1013,
243 czasPomiaru: new Date()
244});
245
246const monitorRexy = new MonitoringZdrowiaDinozaura("HEALTH-TREX-01", "TREX-01", "Tyrannosaurus");
247monitorRexy.zapisz({
248 temperaturaCiała: 38.2,
249 tętno: 72,
250 ciśnienieKrwi: [135, 85],
251 poziomStresu: 3,
252 poziomAktywności: 4,
253 czasPomiaru: new Date()
254});
255
256// Symulacja problemu zdrowotnego
257monitorRexy.zapisz({
258 temperaturaCiała: 40.1, // Gorączka!
259 tętno: 98, // Przyspieszone tętno
260 ciśnienieKrwi: [155, 95], // Podwyższone ciśnienie
261 poziomStresu: 8, // Wysoki poziom stresu
262 poziomAktywności: 2, // Niska aktywność
263 czasPomiaru: new Date()
264});
265
266console.log(monitorRexy.rekomendujInterwencję());Łączenie deklaracji interfejsów
Jedną z unikalnych cech TypeScript jest możliwość łączenia wielu deklaracji tego samego interfejsu - podobnie jak naukowcy z Parku Jurajskiego mogą aktualizować swoje protokoły postępowania w miarę zdobywania nowych informacji:
1// Początkowa definicja protokołu bezpieczeństwa
2interface ProtokółBezpieczeństwa {
3 id: string;
4 nazwa: string;
5 poziomZagrożenia: 1 | 2 | 3 | 4 | 5;
6 proceduraEwakuacji: string;
7}
8
9// Późniejsze rozszerzenie tego samego interfejsu o dodatkowe informacje
10interface ProtokółBezpieczeństwa {
11 personelOdpowiedzialny: string[];
12 czasReakcji: number; // w minutach
13}
14
15// Jeszcze późniejsze rozszerzenie o procedury komunikacji
16interface ProtokółBezpieczeństwa {
17 kanałyKomunikacji: string[];
18 wiadomośćAlarmowa: string;
19}
20
21// Teraz ProtokółBezpieczeństwa ma wszystkie pola ze wszystkich trzech deklaracji
22const protokółAlfa: ProtokółBezpieczeństwa = {
23 id: "PROTO-ALFA",
24 nazwa: "Protokół Alfa - Ucieczka drapieżnika",
25 poziomZagrożenia: 5,
26 proceduraEwakuacji: "Natychmiastowa ewakuacja wszystkich obszarów publicznych do bunkrów bezpieczeństwa",
27 personelOdpowiedzialny: ["Kierownik bezpieczeństwa", "Kierownik parku", "Główny treser"],
28 czasReakcji: 5,
29 kanałyKomunikacji: ["Radio", "System PA", "Alarm dźwiękowy", "Powiadomienia SMS"],
30 wiadomośćAlarmowa: "UWAGA! PROTOKÓŁ ALFA! To nie jest ćwiczenie. Proszę natychmiast udać się do najbliższego schronu."
31};
32
33// Funkcja do wyświetlania protokołu
34function wyświetlProtokół(protokół: ProtokółBezpieczeństwa): void {
35 console.log(`=== ${protokół.nazwa.toUpperCase()} ===`);
36 console.log(`ID: ${protokół.id}`);
37 console.log(`Poziom zagrożenia: ${protokół.poziomZagrożenia}`);
38 console.log(`Czas reakcji: ${protokół.czasReakcji} minut`);
39 console.log(`\nProcedura ewakuacji:\n${protokół.proceduraEwakuacji}`);
40 console.log(`\nPersonel odpowiedzialny:`);
41 protokół.personelOdpowiedzialny.forEach(osoba => console.log(`- ${osoba}`));
42 console.log(`\nKanały komunikacji: ${protokół.kanałyKomunikacji.join(", ")}`);
43 console.log(`\nWiadomość alarmowa: "${protokół.wiadomośćAlarmowa}"`);
44}
45
46wyświetlProtokół(protokółAlfa);Kompatybilność strukturalna interfejsów
TypeScript używa kompatybilności strukturalnej, co oznacza, że typy są kompatybilne, jeśli mają tę samą strukturę, niezależnie od ich nazw. To jak w Parku Jurajskim - jeśli dinozaur zachowuje się jak T-Rex i wygląda jak T-Rex, to z perspektywy systemu bezpieczeństwa powinien być traktowany jak T-Rex:
1// Definicja interfejsu dinozaura drapieżnego
2interface DrapieżnyDinozaur {
3 id: string;
4 nazwa: string;
5 siłaUgryzienia: number;
6 prędkośćBiegu: number;
7 poziomAgresji: number;
8}
9
10// Całkowicie osobna definicja, bez żadnego formalnego związku z powyższym interfejsem
11interface NiebezpiecznyStwór {
12 id: string;
13 nazwa: string;
14 siłaUgryzienia: number;
15 prędkośćBiegu: number;
16 poziomAgresji: number;
17 // Dodatkowe pole, które nie przeszkadza w kompatybilności
18 pochodzenie?: string;
19}
20
21function ocenZagrożenie(dino: DrapieżnyDinozaur): number {
22 return (dino.siłaUgryzienia / 1000) + (dino.prędkośćBiegu / 10) + dino.poziomAgresji;
23}
24
25// Tworzymy obiekt zgodny z NiebezpiecznyStwór
26const indominus: NiebezpiecznyStwór = {
27 id: "IND-01",
28 nazwa: "Indominus Rex",
29 siłaUgryzienia: 15000,
30 prędkośćBiegu: 48,
31 poziomAgresji: 10,
32 pochodzenie: "Laboratorium"
33};
34
35// Mimo że indominus jest typu NiebezpiecznyStwór, można go użyć tam,
36// gdzie oczekiwany jest DrapieżnyDinozaur, dzięki kompatybilności strukturalnej
37const poziomZagrożenia = ocenZagrożenie(indominus);
38console.log(`Poziom zagrożenia dla ${indominus.nazwa}: ${poziomZagrożenia}`);
39
40// Funkcja oczekująca obiektu zgodnego z NiebezpiecznyStwór
41function opisZagrożenie(stwór: NiebezpiecznyStwór): string {
42 let opis = `${stwór.nazwa} to niezwykle niebezpieczne stworzenie.\n`;
43 opis += `Siła ugryzienia: ${stwór.siłaUgryzienia} PSI\n`;
44 opis += `Prędkość biegu: ${stwór.prędkośćBiegu} km/h\n`;
45 opis += `Poziom agresji: ${stwór.poziomAgresji}/10\n`;
46
47 if (stwór.pochodzenie) {
48 opis += `Stworzony w: ${stwór.pochodzenie}`;
49 }
50
51 return opis;
52}
53
54// Tworzymy obiekt zgodny z DrapieżnyDinozaur
55const velociraptor: DrapieżnyDinozaur = {
56 id: "VEL-01",
57 nazwa: "Blue",
58 siłaUgryzienia: 4000,
59 prędkośćBiegu: 65,
60 poziomAgresji: 7
61};
62
63// Możemy użyć obiektu typu DrapieżnyDinozaur w funkcji oczekującej NiebezpiecznyStwór
64// Działa, ponieważ velociraptor ma wszystkie wymagane właściwości
65const opisRaptora = opisZagrożenie(velociraptor);
66console.log(opisRaptora);Interfejsy vs. typy abstrakcyjne
Porównajmy interfejsy z typami abstrakcyjnymi (abstract classes) w kontekście Parku Jurajskiego:
1// Interfejs dla systemów bezpieczeństwa
2interface SystemBezpieczeństwaInterface {
3 nazwa: string;
4 aktywuj(): void;
5 dezaktywuj(): void;
6 testuj(): boolean;
7}
8
9// Abstrakcyjna klasa dla systemów bezpieczeństwa
10abstract class SystemBezpieczeństwaAbstractClass {
11 constructor(public nazwa: string, protected poziomBezpieczeństwa: number) {}
12
13 abstract aktywuj(): void;
14 abstract dezaktywuj(): void;
15
16 // Wspólna implementacja dla wszystkich klas pochodnych
17 testuj(): boolean {
18 console.log(`Testowanie systemu ${this.nazwa}...`);
19 return Math.random() > 0.1; // 90% szans na sukces testu
20 }
21
22 // Metoda dostępna dla wszystkich klas pochodnych
23 pobierzPoziom(): number {
24 return this.poziomBezpieczeństwa;
25 }
26}
27
28// Implementacja interfejsu
29class SkaneryRuchu implements SystemBezpieczeństwaInterface {
30 constructor(public nazwa: string) {}
31
32 aktywuj(): void {
33 console.log(`Skanery ruchu ${this.nazwa} zostały aktywowane`);
34 }
35
36 dezaktywuj(): void {
37 console.log(`Skanery ruchu ${this.nazwa} zostały dezaktywowane`);
38 }
39
40 testuj(): boolean {
41 console.log(`Testowanie skanerów ruchu ${this.nazwa}...`);
42 return true;
43 }
44}
45
46// Rozszerzenie klasy abstrakcyjnej
47class ElektroniczneOgrodzenie extends SystemBezpieczeństwaAbstractClass {
48 private napięcie: number;
49
50 constructor(nazwa: string, poziomBezpieczeństwa: number, napięcie: number) {
51 super(nazwa, poziomBezpieczeństwa);
52 this.napięcie = napięcie;
53 }
54
55 aktywuj(): void {
56 console.log(`Ogrodzenie elektryczne ${this.nazwa} aktywowane z napięciem ${this.napięcie}V`);
57 }
58
59 dezaktywuj(): void {
60 console.log(`Ogrodzenie elektryczne ${this.nazwa} dezaktywowane`);
61 }
62
63 zwiększNapięcie(o: number): void {
64 this.napięcie += o;
65 console.log(`Zwiększono napięcie ogrodzenia ${this.nazwa} do ${this.napięcie}V`);
66 }
67}
68
69// Użycie obu podejść
70const skanery = new SkaneryRuchu("Strefa T-Rex");
71skanery.aktywuj();
72skanery.testuj();
73
74const ogrodzenie = new ElektroniczneOgrodzenie("Padok wschodni", 4, 10000);
75ogrodzenie.aktywuj();
76ogrodzenie.zwiększNapięcie(2000);
77console.log(`Poziom bezpieczeństwa ogrodzenia: ${ogrodzenie.pobierzPoziom()}`);Główne różnice:
| Cecha | Interfejs | Klasa abstrakcyjna |
|---|---|---|
| Implementacja | Tylko definicja, bez implementacji | Może zawierać implementację metod |
| Dziedziczenie | Klasa może implementować wiele interfejsów | Klasa może dziedziczyć tylko z jednej klasy abstrakcyjnej |
| Konstruktor | Nie ma konstruktora | Może mieć konstruktor |
| Kod w runtime | Interfejsy nie generują kodu JavaScript | Klasy abstrakcyjne generują kod JavaScript |
| Modyfikatory dostępu | Nie można używać modyfikatorów (private, protected) | Można używać modyfikatorów dostępu |
| Użycie | Definicja kontraktu | Definicja bazowej funkcjonalności z częściową implementacją |
Praktyczne zastosowanie rozszerzania interfejsów
Zobaczmy teraz kompleksowy przykład ilustrujący, jak rozszerzanie interfejsów może być wykorzystane w systemie zarządzania Parkiem Jurajskim:
1// Bazowy interfejs dla wszystkich bytów w parku
2interface BazaParkuJurajskiego {
3 id: string;
4 nazwa: string;
5 dataDodania: Date;
6 ostatniaAktualizacja: Date;
7}
8
9// Rozszerzenie dla żywych stworzeń
10interface ŻyweStworzeniaParku extends BazaParkuJurajskiego {
11 wiek: number;
12 płeć: "samiec" | "samica";
13 statusZdrowia: "doskonały" | "dobry" | "wymaga uwagi" | "chory" | "krytyczny";
14 historiaZdrowia: {
15 data: Date;
16 opis: string;
17 procedura?: string;
18 }[];
19}
20
21// Rozszerzenie dla dinozaurów
22interface DinozaurParku extends ŻyweStworzeniaParku {
23 gatunek: string;
24 eraPochodzenia: string;
25 dieta: "roślinożerca" | "mięsożerca" | "wszystkożerca";
26 poziomNiebezpieczeństwa: 1 | 2 | 3 | 4 | 5;
27 lokalizacja: string; // ID wybiegu
28 cechy: {
29 wysokość: number;
30 długość: number;
31 waga: number;
32 prędkośćMaksymalna: number;
33 siłaUgryzienia?: number; // Tylko dla drapieżników
34 };
35}
36
37// Rozszerzenie dla pracowników
38interface PracownikParku extends ŻyweStworzeniaParku {
39 imię: string;
40 nazwisko: string;
41 stanowisko: string;
42 poziomDostępu: 1 | 2 | 3 | 4 | 5;
43 działy: string[];
44 adres: {
45 ulica: string;
46 miasto: string;
47 kodPocztowy: string;
48 kraj: string;
49 };
50 kontakt: {
51 telefon: string;
52 email: string;
53 };
54}
55
56// Interfejs dla lokalizacji w parku
57interface LokalizacjaParku extends BazaParkuJurajskiego {
58 typ: "wybieg" | "budynek" | "atrakcja" | "laboratorium" | "strefa publiczna";
59 współrzędne: {
60 szerokość: number;
61 długość: number;
62 };
63 powierzchnia: number; // w metrach kwadratowych
64 pojemność: number;
65 poziomDostępu: 1 | 2 | 3 | 4 | 5;
66 systemyBezpieczeństwa: string[]; // Lista ID systemów bezpieczeństwa
67 status: "aktywny" | "nieaktywny" | "w konserwacji" | "w budowie";
68}
69
70// Interfejs dla wybiegu dinozaurów
71interface WybiegDinozaurów extends LokalizacjaParku {
72 mieszkańcy: string[]; // Lista ID dinozaurów
73 typŚrodowiska: "las" | "sawanna" | "bagno" | "góry" | "wybrzeże" | "akwen";
74 specjalneWymagania?: string[];
75 cechy: {
76 wysokośćOgrodzenia: number;
77 napięcieOgrodzenia?: number; // Dla drapieżników
78 liczbaSkanerów: number;
79 liczbaKamer: number;
80 };
81}
82
83// Interfejs dla laboratoriów
84interface LaboratoriumParku extends LokalizacjaParku {
85 specjalizacja: "genetyka" | "behawioralna" | "weterynaria" | "mikrobiologia";
86 wyposażenie: string[];
87 personel: string[]; // Lista ID pracowników
88 projektyBadawcze: {
89 id: string;
90 nazwa: string;
91 start: Date;
92 koniec?: Date;
93 status: "planowany" | "w trakcie" | "zakończony" | "wstrzymany";
94 }[];
95}
96
97// System zarządzania parkiem
98class ParkJurajskiSystem {
99 private dinozaury: Map<string, DinozaurParku> = new Map();
100 private pracownicy: Map<string, PracownikParku> = new Map();
101 private lokalizacje: Map<string, LokalizacjaParku> = new Map();
102 private laboratorium: Map<string, LaboratoriumParku> = new Map();
103 private wybiegi: Map<string, WybiegDinozaurów> = new Map();
104
105 // Metody dla dinozaurów
106 dodajDinozaura(dino: DinozaurParku): void {
107 this.dinozaury.set(dino.id, dino);
108 console.log(`Dodano dinozaura: ${dino.nazwa} (${dino.gatunek})`);
109
110 // Aktualizacja wybiegu
111 if (dino.lokalizacja && this.wybiegi.has(dino.lokalizacja)) {
112 const wybieg = this.wybiegi.get(dino.lokalizacja)!;
113 wybieg.mieszkańcy.push(dino.id);
114 this.wybiegi.set(wybieg.id, wybieg);
115 }
116 }
117
118 aktualizujStatusZdrowiaDinozaura(id: string, nowyStatus: DinozaurParku["statusZdrowia"], opis: string): void {
119 if (!this.dinozaury.has(id)) {
120 console.error(`Dinozaur o ID ${id} nie istnieje`);
121 return;
122 }
123
124 const dino = this.dinozaury.get(id)!;
125 const staryStatus = dino.statusZdrowia;
126 dino.statusZdrowia = nowyStatus;
127 dino.ostatniaAktualizacja = new Date();
128
129 // Dodanie wpisu do historii zdrowia
130 dino.historiaZdrowia.push({
131 data: new Date(),
132 opis: `Zmiana statusu zdrowia z "${staryStatus}" na "${nowyStatus}". ${opis}`
133 });
134
135 this.dinozaury.set(id, dino);
136 console.log(`Zaktualizowano status zdrowia dinozaura ${dino.nazwa} na: ${nowyStatus}`);
137 }
138
139 // Metody dla pracowników
140 dodajPracownika(pracownik: PracownikParku): void {
141 this.pracownicy.set(pracownik.id, pracownik);
142 console.log(`Dodano pracownika: ${pracownik.imię} ${pracownik.nazwisko} (${pracownik.stanowisko})`);
143 }
144
145 // Metody dla lokalizacji
146 dodajWybieg(wybieg: WybiegDinozaurów): void {
147 this.wybiegi.set(wybieg.id, wybieg);
148 this.lokalizacje.set(wybieg.id, wybieg); // Wybieg jest również lokalizacją
149 console.log(`Dodano wybieg: ${wybieg.nazwa}`);
150 }
151
152 dodajLaboratorium(lab: LaboratoriumParku): void {
153 this.laboratorium.set(lab.id, lab);
154 this.lokalizacje.set(lab.id, lab); // Laboratorium jest również lokalizacją
155 console.log(`Dodano laboratorium: ${lab.nazwa} (${lab.specjalizacja})`);
156 }
157
158 // Metody raportowania
159 generujRaportParkuJurajskiego(): string {
160 const raport = [];
161 raport.push("=== RAPORT STANU PARKU JURAJSKIEGO ===");
162 raport.push(`Data raportu: ${new Date().toLocaleString()}`);
163 raport.push(`\nStatystyki ogólne:`);
164 raport.push(`- Liczba dinozaurów: ${this.dinozaury.size}`);
165 raport.push(`- Liczba pracowników: ${this.pracownicy.size}`);
166 raport.push(`- Liczba lokalizacji: ${this.lokalizacje.size}`);
167 raport.push(` - Wybiegi: ${this.wybiegi.size}`);
168 raport.push(` - Laboratoria: ${this.laboratorium.size}`);
169
170 // Statystyki dinozaurów
171 const statystykiZdrowia = {
172 "doskonały": 0,
173 "dobry": 0,
174 "wymaga uwagi": 0,
175 "chory": 0,
176 "krytyczny": 0
177 };
178
179 let liczbaDrapieżników = 0;
180
181 for (const dino of this.dinozaury.values()) {
182 statystykiZdrowia[dino.statusZdrowia]++;
183 if (dino.dieta === "mięsożerca") {
184 liczbaDrapieżników++;
185 }
186 }
187
188 raport.push(`\nStatystyki zdrowia dinozaurów:`);
189 for (const [status, liczba] of Object.entries(statystykiZdrowia)) {
190 raport.push(`- ${status}: ${liczba}`);
191 }
192
193 raport.push(`Drapieżniki: ${liczbaDrapieżników}`);
194 raport.push(`Roślinożercy: ${this.dinozaury.size - liczbaDrapieżników}`);
195
196 // Statystyki wybiegów
197 const wybiegiZeStatusem = {
198 "aktywny": 0,
199 "nieaktywny": 0,
200 "w konserwacji": 0,
201 "w budowie": 0
202 };
203
204 for (const wybieg of this.wybiegi.values()) {
205 wybiegiZeStatusem[wybieg.status]++;
206 }
207
208 raport.push(`\nStatystyki wybiegów:`);
209 for (const [status, liczba] of Object.entries(wybiegiZeStatusem)) {
210 raport.push(`- ${status}: ${liczba}`);
211 }
212
213 return raport.join("\n");
214 }
215}
216
217// Przykład użycia
218const park = new ParkJurajskiSystem();
219
220// Dodanie wybiegu
221park.dodajWybieg({
222 id: "WYB-001",
223 nazwa: "Padok T-Rex",
224 dataDodania: new Date(2020, 0, 15),
225 ostatniaAktualizacja: new Date(),
226 typ: "wybieg",
227 współrzędne: {
228 szerokość: 20.123,
229 długość: -75.456
230 },
231 powierzchnia: 50000, // 5 hektarów
232 pojemność: 2,
233 poziomDostępu: 5,
234 systemyBezpieczeństwa: ["SEC-001", "SEC-002", "SEC-003"],
235 status: "aktywny",
236 mieszkańcy: [],
237 typŚrodowiska: "las",
238 cechy: {
239 wysokośćOgrodzenia: 8,
240 napięcieOgrodzenia: 10000,
241 liczbaSkanerów: 24,
242 liczbaKamer: 36
243 }
244});
245
246// Dodanie laboratorium
247park.dodajLaboratorium({
248 id: "LAB-001",
249 nazwa: "Laboratorium Genetyczne im. Dr. Wu",
250 dataDodania: new Date(2018, 5, 10),
251 ostatniaAktualizacja: new Date(),
252 typ: "laboratorium",
253 współrzędne: {
254 szerokość: 20.124,
255 długość: -75.458
256 },
257 powierzchnia: 2000,
258 pojemność: 30,
259 poziomDostępu: 4,
260 systemyBezpieczeństwa: ["SEC-101", "SEC-102"],
261 status: "aktywny",
262 specjalizacja: "genetyka",
263 wyposażenie: ["Sekwencer DNA", "Izolatory próbek", "Komora kriogeniczna"],
264 personel: [],
265 projektyBadawcze: [
266 {
267 id: "PROJ-001",
268 nazwa: "Wzmacnianie genów odporności u Triceratopsów",
269 start: new Date(2022, 3, 15),
270 status: "w trakcie"
271 }
272 ]
273});
274
275// Dodanie dinozaura
276park.dodajDinozaura({
277 id: "DINO-001",
278 nazwa: "Rexy",
279 dataDodania: new Date(2020, 5, 20),
280 ostatniaAktualizacja: new Date(),
281 wiek: 7,
282 płeć: "samica",
283 statusZdrowia: "dobry",
284 historiaZdrowia: [
285 {
286 data: new Date(2020, 5, 20),
287 opis: "Badanie początkowe. Wszystkie parametry w normie."
288 },
289 {
290 data: new Date(2021, 2, 15),
291 opis: "Rutynowe badanie. Niewielkie podniesienie poziomu trójglicerydów.",
292 procedura: "Zmiana diety na bogatszą w kwasy Omega-3"
293 }
294 ],
295 gatunek: "Tyrannosaurus Rex",
296 eraPochodzenia: "Kreda późna",
297 dieta: "mięsożerca",
298 poziomNiebezpieczeństwa: 5,
299 lokalizacja: "WYB-001",
300 cechy: {
301 wysokość: 4.5,
302 długość: 12,
303 waga: 7200,
304 prędkośćMaksymalna: 32,
305 siłaUgryzienia: 12800
306 }
307});
308
309// Dodanie pracownika
310park.dodajPracownika({
311 id: "EMP-001",
312 nazwa: "Pracownik",
313 dataDodania: new Date(2019, 1, 10),
314 ostatniaAktualizacja: new Date(),
315 wiek: 42,
316 płeć: "mężczyzna",
317 statusZdrowia: "doskonały",
318 historiaZdrowia: [
319 {
320 data: new Date(2022, 1, 5),
321 opis: "Rutynowe badanie okresowe. Wszystkie parametry w normie."
322 }
323 ],
324 imię: "Owen",
325 nazwisko: "Grady",
326 stanowisko: "Treser Raptorów",
327 poziomDostępu: 4,
328 działy: ["Badania Behawioralne", "Opieka nad Zwierzętami"],
329 adres: {
330 ulica: "Bungalow 12",
331 miasto: "Isla Nublar",
332 kodPocztowy: "00000",
333 kraj: "Kostaryka"
334 },
335 kontakt: {
336 telefon: "+1-555-123-4567",
337 email: "owen.grady@jurassicworld.com"
338 }
339});
340
341// Aktualizacja statusu zdrowia dinozaura
342park.aktualizujStatusZdrowiaDinozaura(
343 "DINO-001",
344 "wymaga uwagi",
345 "Zauważono zmniejszony apetyt i lekką gorączkę."
346);
347
348// Generowanie raportu
349console.log(park.generujRaportParkuJurajskiego());Podsumowanie
Rozszerzanie interfejsów w TypeScript to potężna technika, która pozwala na modelowanie złożonych hierarchii typów i relacji między nimi. W kontekście Parku Jurajskiego, umożliwia nam to precyzyjne modelowanie różnorodnych bytów i systemów, od dinozaurów, przez pracowników, aż po zaawansowane systemy monitoringu i bezpieczeństwa.
| Technika | Zastosowanie w Parku Jurajskim |
|---|---|
| Podstawowe rozszerzanie interfejsów | Hierarchia stworzeń od bazowych do specyficznych gatunków |
| Rozszerzanie wielu interfejsów | Tworzenie hybrydowych typów, jak morskie drapieżniki |
| Generyczne rozszerzenia interfejsów | Systemy monitoringu różnych typów danych |
| Łączenie deklaracji interfejsów | Aktualizowanie protokołów bezpieczeństwa |
| Kompatybilność strukturalna | Jednolite traktowanie różnych typów dinozaurów przez systemy bezpieczeństwa |
Dr. Wu podsumowuje: "Rozszerzanie interfejsów w TypeScript przypomina naszą pracę nad nowymi gatunkami dinozaurów. Zaczynamy od podstawowej struktury i rozszerzamy ją o nowe cechy i funkcjonalności, tworząc coraz bardziej wyspecjalizowane i złożone byty. Ta elastyczność i możliwość ponownego wykorzystania istniejących definicji sprawia, że możemy tworzyć spójne, ale jednocześnie wysoce zróżnicowane systemy - kluczowe zarówno w genetyce jak i w programowaniu."
Kod do tej lekcji: index.ts
1// Rozszerzanie interfejsów w Parku Jurajskim
2console.log("Rozszerzanie interfejsów TypeScript\n");
3
4// ===========================================
5// 1. Podstawowe rozszerzanie
6// ===========================================
7console.log("=== PODSTAWOWE ROZSZERZANIE ===");
8
9// Bazowy interfejs
10interface Animal {
11 name: string;
12 age: number;
13 weight: number;
14}
15
16// Rozszerzenie interfejsu
17interface Dinosaur extends Animal {
18 species: string;
19 period: "Triassic" | "Jurassic" | "Cretaceous";
20 diet: "carnivore" | "herbivore" | "omnivore";
21}
22
23const trex: Dinosaur = {
24 name: "Rexy",
25 age: 5,
26 weight: 8000,
27 species: "Tyrannosaurus Rex",
28 period: "Cretaceous",
29 diet: "carnivore"
30};
31
32console.log("T-Rex:", trex);
33
34// ===========================================
35// 2. Wielokrotne rozszerzanie
36// ===========================================
37console.log("\n=== WIELOKROTNE ROZSZERZANIE ===");
38
39interface HasName {
40 name: string;
41}
42
43interface HasWeight {
44 weight: number;
45}
46
47interface HasDiet {
48 diet: "carnivore" | "herbivore" | "omnivore";
49}
50
51// Rozszerzenie wielu interfejsów
52interface CompleteDinosaur extends HasName, HasWeight, HasDiet {
53 species: string;
54 dangerLevel: number;
55}
56
57const velociraptor: CompleteDinosaur = {
58 name: "Blue",
59 weight: 350,
60 diet: "carnivore",
61 species: "Velociraptor",
62 dangerLevel: 8
63};
64
65console.log("Velociraptor:", velociraptor);
66
67// ===========================================
68// 3. Nadpisywanie właściwości
69// ===========================================
70console.log("\n=== NADPISYWANIE WŁAŚCIWOŚCI ===");
71
72interface BasicDinosaur {
73 id: string | number;
74 name: string;
75}
76
77// Zawężenie typu przy rozszerzaniu
78interface SpecificDinosaur extends BasicDinosaur {
79 id: string; // zawęzi type z string | number do string
80 trackingCode: string;
81}
82
83const trackedDino: SpecificDinosaur = {
84 id: "DINO-001", // musi być string
85 name: "Rex",
86 trackingCode: "TR-XYZ-123"
87};
88
89console.log("Tracked dinosaur:", trackedDino);
90
91// ===========================================
92// 4. Rozszerzanie z dodatkowymi metodami
93// ===========================================
94console.log("\n=== ROZSZERZANIE Z METODAMI ===");
95
96interface LivingBeing {
97 name: string;
98 eat(amount: number): void;
99 sleep(hours: number): void;
100}
101
102interface Predator extends LivingBeing {
103 hunt(prey: string): string;
104 roar(): string;
105 attackPower: number;
106}
107
108class TyrannosaurusRex implements Predator {
109 name: string;
110 attackPower: number;
111 energy: number = 100;
112
113 constructor(name: string) {
114 this.name = name;
115 this.attackPower = 95;
116 }
117
118 eat(amount: number): void {
119 this.energy += amount * 2;
120 if (this.energy > 100) this.energy = 100;
121 console.log(`${this.name} ate ${amount}kg. Energy: ${this.energy}`);
122 }
123
124 sleep(hours: number): void {
125 this.energy += hours * 10;
126 if (this.energy > 100) this.energy = 100;
127 console.log(`${this.name} slept ${hours}h. Energy: ${this.energy}`);
128 }
129
130 hunt(prey: string): string {
131 this.energy -= 20;
132 return `${this.name} hunts ${prey}! Energy: ${this.energy}`;
133 }
134
135 roar(): string {
136 this.energy -= 5;
137 return `${this.name} roars: ROAAAAAR! (${this.attackPower} power)`;
138 }
139}
140
141const rexy = new TyrannosaurusRex("Rexy");
142console.log(rexy.roar());
143console.log(rexy.hunt("Triceratops"));
144rexy.eat(50);
145
146// ===========================================
147// 5. Łańcuchowe rozszerzanie
148// ===========================================
149console.log("\n=== ŁAŃCUCHOWE ROZSZERZANIE ===");
150
151interface Entity {
152 id: string;
153}
154
155interface LivingEntity extends Entity {
156 isAlive: boolean;
157}
158
159interface BreathingEntity extends LivingEntity {
160 oxygenLevel: number;
161}
162
163interface IntelligentEntity extends BreathingEntity {
164 intelligenceLevel: number;
165 canCommunicate: boolean;
166}
167
168const velociRaptor: IntelligentEntity = {
169 id: "VR-001",
170 isAlive: true,
171 oxygenLevel: 98,
172 intelligenceLevel: 85,
173 canCommunicate: true
174};
175
176console.log("Intelligent entity:", velociRaptor);
177
178// ===========================================
179// 6. Generics z rozszerzaniem
180// ===========================================
181console.log("\n=== GENERICS Z ROZSZERZANIEM ===");
182
183interface Trackable {
184 trackingId: string;
185 lastSeen: Date;
186}
187
188interface DataEntry extends Trackable {
189 data: any;
190}
191
192interface DinosaurEntry<T> extends DataEntry {
193 dinosaurData: T;
194}
195
196interface TRexData {
197 species: "Tyrannosaurus Rex";
198 teethCount: number;
199 biteForce: number;
200}
201
202const trexEntry: DinosaurEntry<TRexData> = {
203 trackingId: "TRK-001",
204 lastSeen: new Date(),
205 data: { notes: "Healthy specimen" },
206 dinosaurData: {
207 species: "Tyrannosaurus Rex",
208 teethCount: 60,
209 biteForce: 12800
210 }
211};
212
213console.log("T-Rex entry:", trexEntry);
214
215// ===========================================
216// 7. Rozszerzanie z Index Signatures
217// ===========================================
218console.log("\n=== INDEX SIGNATURES ===");
219
220interface BaseConfig {
221 version: string;
222 enabled: boolean;
223}
224
225interface ExtendedConfig extends BaseConfig {
226 [key: string]: any; // dodatkowe dynamiczne właściwości
227}
228
229const parkConfig: ExtendedConfig = {
230 version: "2.0",
231 enabled: true,
232 maxCapacity: 10000,
233 securityLevel: "high",
234 maintenanceMode: false
235};
236
237console.log("Park config:", parkConfig);
238
239// ===========================================
240// 8. Rozszerzanie vs Intersection
241// ===========================================
242console.log("\n=== EXTENDS VS INTERSECTION ===");
243
244// Extends (interfejsy)
245interface A {
246 a: string;
247}
248
249interface B extends A {
250 b: number;
251}
252
253const objB: B = {
254 a: "value",
255 b: 123
256};
257
258// Intersection (typy)
259type C = {
260 c: boolean;
261};
262
263type D = C & {
264 d: string[];
265};
266
267const objD: D = {
268 c: true,
269 d: ["item1", "item2"]
270};
271
272console.log("Interface extension:", objB);
273console.log("Type intersection:", objD);
274
275// ===========================================
276// 9. Praktyczny przykład - System Parku
277// ===========================================
278console.log("\n=== SYSTEM PARKU ===");
279
280interface Identifiable {
281 id: string;
282}
283
284interface Timestamped {
285 createdAt: Date;
286 updatedAt: Date;
287}
288
289interface Locatable {
290 latitude: number;
291 longitude: number;
292}
293
294interface ParkEntity extends Identifiable, Timestamped, Locatable {
295 name: string;
296 status: "active" | "inactive" | "maintenance";
297}
298
299interface Paddock extends ParkEntity {
300 capacity: number;
301 currentOccupancy: number;
302 securityLevel: "low" | "medium" | "high" | "critical";
303}
304
305interface DinosaurPark extends ParkEntity {
306 paddocks: Paddock[];
307 totalDinosaurs: number;
308 isOpen: boolean;
309}
310
311const jurassicPark: DinosaurPark = {
312 id: "PARK-001",
313 createdAt: new Date("1993-06-11"),
314 updatedAt: new Date(),
315 latitude: 22.3964,
316 longitude: -159.5261,
317 name: "Jurassic Park",
318 status: "active",
319 paddocks: [],
320 totalDinosaurs: 0,
321 isOpen: false
322};
323
324console.log("Jurassic Park:", jurassicPark);
325
326// ===========================================
327// 10. Podsumowanie
328// ===========================================
329console.log("\n=== PODSUMOWANIE ===");
330console.log("extends - rozszerzanie interfejsów");
331console.log("Wielokrotne rozszerzanie - dziedziczenie wielu interfejsów");
332console.log("Nadpisywanie - zawężanie typów");
333console.log("Metody - dodawanie funkcjonalności");
334console.log("Łańcuchowe - budowanie hierarchii");
335console.log("Generics - parametryczne rozszerzanie");
336
337console.log("\nRozszerzanie interfejsów 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. Co dzieje się, gdy interfejs B extends interfejs A?
2. Czy interfejs w TypeScript może rozszerzać wiele interfejsów jednocześnie?
Zadania praktyczne w grze
- Edytor kodu
Stwórz interfejs bazowy Animal i rozszerz go do Dinosaur z dodatkowymi polami era i diet.
- Układanie w poziomie
Ułóż elementy rozszerzania interfejsu w TypeScript: