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.
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};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 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);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ę());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(`
40Procedura ewakuacji:
41${protokół.proceduraEwakuacji}`);
42 console.log(`
43Personel odpowiedzialny:`);
44 protokół.personelOdpowiedzialny.forEach(osoba => console.log(`- ${osoba}`));
45 console.log(`
46Kanały komunikacji: ${protokół.kanałyKomunikacji.join(", ")}`);
47 console.log(`
48Wiadomość alarmowa: "${protokół.wiadomośćAlarmowa}"`);
49}
50
51wyświetlProtokół(protokółAlfa);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.
43`;
44 opis += `Siła ugryzienia: ${stwór.siłaUgryzienia} PSI
45`;
46 opis += `Prędkość biegu: ${stwór.prędkośćBiegu} km/h
47`;
48 opis += `Poziom agresji: ${stwór.poziomAgresji}/10
49`;
50
51 if (stwór.pochodzenie) {
52 opis += `Stworzony w: ${stwór.pochodzenie}`;
53 }
54
55 return opis;
56}
57
58// Tworzymy obiekt zgodny z DrapieżnyDinozaur
59const velociraptor: DrapieżnyDinozaur = {
60 id: "VEL-01",
61 nazwa: "Blue",
62 siłaUgryzienia: 4000,
63 prędkośćBiegu: 65,
64 poziomAgresji: 7
65};
66
67// Możemy użyć obiektu typu DrapieżnyDinozaur w funkcji oczekującej NiebezpiecznyStwór
68// Działa, ponieważ velociraptor ma wszystkie wymagane właściwości
69const opisRaptora = opisZagrożenie(velociraptor);
70console.log(opisRaptora);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()}`);| 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ą |
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(`
164Statystyki ogólne:`);
165 raport.push(`- Liczba dinozaurów: ${this.dinozaury.size}`);
166 raport.push(`- Liczba pracowników: ${this.pracownicy.size}`);
167 raport.push(`- Liczba lokalizacji: ${this.lokalizacje.size}`);
168 raport.push(` - Wybiegi: ${this.wybiegi.size}`);
169 raport.push(` - Laboratoria: ${this.laboratorium.size}`);
170
171 // Statystyki dinozaurów
172 const statystykiZdrowia = {
173 "doskonały": 0,
174 "dobry": 0,
175 "wymaga uwagi": 0,
176 "chory": 0,
177 "krytyczny": 0
178 };
179
180 let liczbaDrapieżników = 0;
181
182 for (const dino of this.dinozaury.values()) {
183 statystykiZdrowia[dino.statusZdrowia]++;
184 if (dino.dieta === "mięsożerca") {
185 liczbaDrapieżników++;
186 }
187 }
188
189 raport.push(`
190Statystyki zdrowia dinozaurów:`);
191 for (const [status, liczba] of Object.entries(statystykiZdrowia)) {
192 raport.push(`- ${status}: ${liczba}`);
193 }
194
195 raport.push(`Drapieżniki: ${liczbaDrapieżników}`);
196 raport.push(`Roślinożercy: ${this.dinozaury.size - liczbaDrapieżników}`);
197
198 // Statystyki wybiegów
199 const wybiegiZeStatusem = {
200 "aktywny": 0,
201 "nieaktywny": 0,
202 "w konserwacji": 0,
203 "w budowie": 0
204 };
205
206 for (const wybieg of this.wybiegi.values()) {
207 wybiegiZeStatusem[wybieg.status]++;
208 }
209
210 raport.push(`
211Statystyki wybiegów:`);
212 for (const [status, liczba] of Object.entries(wybiegiZeStatusem)) {
213 raport.push(`- ${status}: ${liczba}`);
214 }
215
216 return raport.join("
217");
218 }
219}
220
221// Przykład użycia
222const park = new ParkJurajskiSystem();
223
224// Dodanie wybiegu
225park.dodajWybieg({
226 id: "WYB-001",
227 nazwa: "Padok T-Rex",
228 dataDodania: new Date(2020, 0, 15),
229 ostatniaAktualizacja: new Date(),
230 typ: "wybieg",
231 współrzędne: {
232 szerokość: 20.123,
233 długość: -75.456
234 },
235 powierzchnia: 50000, // 5 hektarów
236 pojemność: 2,
237 poziomDostępu: 5,
238 systemyBezpieczeństwa: ["SEC-001", "SEC-002", "SEC-003"],
239 status: "aktywny",
240 mieszkańcy: [],
241 typŚrodowiska: "las",
242 cechy: {
243 wysokośćOgrodzenia: 8,
244 napięcieOgrodzenia: 10000,
245 liczbaSkanerów: 24,
246 liczbaKamer: 36
247 }
248});
249
250// Dodanie laboratorium
251park.dodajLaboratorium({
252 id: "LAB-001",
253 nazwa: "Laboratorium Genetyczne im. Dr. Wu",
254 dataDodania: new Date(2018, 5, 10),
255 ostatniaAktualizacja: new Date(),
256 typ: "laboratorium",
257 współrzędne: {
258 szerokość: 20.124,
259 długość: -75.458
260 },
261 powierzchnia: 2000,
262 pojemność: 30,
263 poziomDostępu: 4,
264 systemyBezpieczeństwa: ["SEC-101", "SEC-102"],
265 status: "aktywny",
266 specjalizacja: "genetyka",
267 wyposażenie: ["Sekwencer DNA", "Izolatory próbek", "Komora kriogeniczna"],
268 personel: [],
269 projektyBadawcze: [
270 {
271 id: "PROJ-001",
272 nazwa: "Wzmacnianie genów odporności u Triceratopsów",
273 start: new Date(2022, 3, 15),
274 status: "w trakcie"
275 }
276 ]
277});
278
279// Dodanie dinozaura
280park.dodajDinozaura({
281 id: "DINO-001",
282 nazwa: "Rexy",
283 dataDodania: new Date(2020, 5, 20),
284 ostatniaAktualizacja: new Date(),
285 wiek: 7,
286 płeć: "samica",
287 statusZdrowia: "dobry",
288 historiaZdrowia: [
289 {
290 data: new Date(2020, 5, 20),
291 opis: "Badanie początkowe. Wszystkie parametry w normie."
292 },
293 {
294 data: new Date(2021, 2, 15),
295 opis: "Rutynowe badanie. Niewielkie podniesienie poziomu trójglicerydów.",
296 procedura: "Zmiana diety na bogatszą w kwasy Omega-3"
297 }
298 ],
299 gatunek: "Tyrannosaurus Rex",
300 eraPochodzenia: "Kreda późna",
301 dieta: "mięsożerca",
302 poziomNiebezpieczeństwa: 5,
303 lokalizacja: "WYB-001",
304 cechy: {
305 wysokość: 4.5,
306 długość: 12,
307 waga: 7200,
308 prędkośćMaksymalna: 32,
309 siłaUgryzienia: 12800
310 }
311});
312
313// Dodanie pracownika
314park.dodajPracownika({
315 id: "EMP-001",
316 nazwa: "Pracownik",
317 dataDodania: new Date(2019, 1, 10),
318 ostatniaAktualizacja: new Date(),
319 wiek: 42,
320 płeć: "mężczyzna",
321 statusZdrowia: "doskonały",
322 historiaZdrowia: [
323 {
324 data: new Date(2022, 1, 5),
325 opis: "Rutynowe badanie okresowe. Wszystkie parametry w normie."
326 }
327 ],
328 imię: "Owen",
329 nazwisko: "Grady",
330 stanowisko: "Treser Raptorów",
331 poziomDostępu: 4,
332 działy: ["Badania Behawioralne", "Opieka nad Zwierzętami"],
333 adres: {
334 ulica: "Bungalow 12",
335 miasto: "Isla Nublar",
336 kodPocztowy: "00000",
337 kraj: "Kostaryka"
338 },
339 kontakt: {
340 telefon: "+1-555-123-4567",
341 email: "owen.grady@jurassicworld.com"
342 }
343});
344
345// Aktualizacja statusu zdrowia dinozaura
346park.aktualizujStatusZdrowiaDinozaura(
347 "DINO-001",
348 "wymaga uwagi",
349 "Zauważono zmniejszony apetyt i lekką gorączkę."
350);
351
352// Generowanie raportu
353console.log(park.generujRaportParkuJurajskiego());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."