Kurs JavaScript i TypeScript · Moduł 6: Podstawy TypeScript

Rozszerzanie interfejsów

18 min czytania
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:

CechaInterfejsKlasa abstrakcyjna
ImplementacjaTylko definicja, bez implementacjiMoże zawierać implementację metod
DziedziczenieKlasa może implementować wiele interfejsówKlasa może dziedziczyć tylko z jednej klasy abstrakcyjnej
KonstruktorNie ma konstruktoraMoże mieć konstruktor
Kod w runtimeInterfejsy nie generują kodu JavaScriptKlasy abstrakcyjne generują kod JavaScript
Modyfikatory dostępuNie można używać modyfikatorów (private, protected)Można używać modyfikatorów dostępu
UżycieDefinicja kontraktuDefinicja 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.

TechnikaZastosowanie w Parku Jurajskim
Podstawowe rozszerzanie interfejsówHierarchia stworzeń od bazowych do specyficznych gatunków
Rozszerzanie wielu interfejsówTworzenie hybrydowych typów, jak morskie drapieżniki
Generyczne rozszerzenia interfejsówSystemy monitoringu różnych typów danych
Łączenie deklaracji interfejsówAktualizowanie protokołów bezpieczeństwa
Kompatybilność strukturalnaJednolite 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. 1. Co dzieje się, gdy interfejs B extends interfejs A?

  2. 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:

Przydatne artykuły