Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Rozszerzanie interfejsów

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(`
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);

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.
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);

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(`
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());

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."

Przejdź do CodeWorlds