Witaj z powrotem w Parku Jurajskim! Dziś, pod czujnym okiem Dr. Henry Wu, będziemy zgłębiać fundamentalną koncepcję TypeScript, która jest równie kluczowa dla organizacji kodu, jak protokoły bezpieczeństwa są dla prawidłowego funkcjonowania parku - interfejsy.
Interfejsy w TypeScript definiują kontrakty w kodzie oraz opisują strukturę obiektów. Są one jak szczegółowe protokoły, które określają, co powinno zawierać dany obiekt. W kontekście Parku Jurajskiego, interfejsy można porównać do standardów bezpieczeństwa i protokołów obsługi dla różnych gatunków dinozaurów - każdy gatunek ma swoje specyficzne wymagania i cechy.
Interfejs definiujemy za pomocą słowa kluczowego
interface:1interface Dinozaur {
2 id: number;
3 nazwa: string;
4 gatunek: string;
5 wiek: number;
6 waga: number;
7}
8
9// Tworzymy obiekt, który musi spełniać wymagania interfejsu
10const rex: Dinozaur = {
11 id: 1,
12 nazwa: "Rex",
13 gatunek: "Tyrannosaurus",
14 wiek: 7,
15 waga: 8000
16};
17
18// Poniższy kod spowodowałby błąd, ponieważ brakuje wymaganych właściwości
19// const raptor: Dinozaur = {
20// id: 2,
21// nazwa: "Blue"
22// };Niektóre właściwości w interfejsie mogą być opcjonalne, oznaczane przez dodanie znaku zapytania (
?). To jak oznaczenie opcjonalnych procedur obsługi dla poszczególnych dinozaurów.1interface OkazDinozaura {
2 id: number;
3 nazwa: string;
4 gatunek: string;
5 tagRFID?: string; // Opcjonalny tag RFID
6 ostatnieBadanie?: Date; // Opcjonalna data ostatniego badania
7 notatki?: string; // Opcjonalne notatki badawcze
8}
9
10// Obiekt spełniający interfejs, ale bez wszystkich opcjonalnych właściwości
11const okaz1: OkazDinozaura = {
12 id: 101,
13 nazwa: "Specimen A",
14 gatunek: "Velociraptor"
15};
16
17// Obiekt z niektórymi opcjonalnymi właściwościami
18const okaz2: OkazDinozaura = {
19 id: 102,
20 nazwa: "Specimen B",
21 gatunek: "Dilophosaurus",
22 tagRFID: "DILO-102",
23 notatki: "Wykazuje agresywne zachowania w obecności nowych obiektów"
24};Niektóre właściwości powinny być modyfikowane tylko przy tworzeniu obiektu. W TypeScript możemy to osiągnąć używając modyfikatora
readonly. To jak procedury bezpieczeństwa, które raz ustalone, nie mogą być zmieniane podczas normalnej operacji.1interface KodGenetyczny {
2 readonly id: string;
3 readonly sekwencja: string;
4 readonly gatunekDawca: string;
5 modyfikacje: string[];
6 dataIzolacji: Date;
7}
8
9// Tworzymy kod genetyczny
10const kodTRex: KodGenetyczny = {
11 id: "GEN-TREX-001",
12 sekwencja: "ACGTTGCAAGCTA...",
13 gatunekDawca: "Żaba zachodnioafrykańska",
14 modyfikacje: ["Uzupełnienie luk DNA", "Korekta genów agresji"],
15 dataIzolacji: new Date("2022-05-15")
16};
17
18// Możemy dodawać nowe modyfikacje
19kodTRex.modyfikacje.push("Wzmocnienie odporności immunologicznej");
20
21// Ale nie możemy zmienić wartości pól readonly
22// kodTRex.id = "GEN-TREX-002"; // Błąd: Cannot assign to 'id' because it is a read-only property
23// kodTRex.sekwencja = "GCTA..."; // Błąd: Cannot assign to 'sekwencja' because it is a read-only propertyInterfejsy mogą również definiować typy funkcji, co jest przydatne przy opisywaniu funkcji zwrotnych lub przekazywanych jako parametry. W kontekście parku, to jak definiowanie standardowych procedur obsługi różnych sytuacji.
1// Interfejs funkcji bezpieczeństwa
2interface SprawdzenieBezpieczeństwa {
3 (poziomZagrożenia: number, strefaParku: string): boolean;
4}
5
6// Implementacja funkcji zgodnej z interfejsem
7const sprawdźDostęp: SprawdzenieBezpieczeństwa = function(poziom, strefa) {
8 // Logika sprawdzania dostępu
9 if (strefa === "Wybieg T-Rex" && poziom < 5) {
10 return false; // Odmowa dostępu do strefy T-Rex dla pracowników poniżej poziomu 5
11 }
12
13 if (strefa.includes("Laboratorium") && poziom < 4) {
14 return false; // Odmowa dostępu do laboratoriów dla pracowników poniżej poziomu 4
15 }
16
17 return true; // Dostęp dozwolony dla pozostałych przypadków
18};
19
20// Użycie funkcji
21console.log(sprawdźDostęp(3, "Wybieg T-Rex")); // false
22console.log(sprawdźDostęp(5, "Wybieg T-Rex")); // true
23console.log(sprawdźDostęp(3, "Obszar publiczny")); // trueInterfejsy mogą definiować typy indeksowalne - użyteczne, gdy mamy obiekty z dynamiczną liczbą właściwości tego samego typu. W kontekście parku, może to być baza danych dinozaurów indeksowana po ich identyfikatorach.
1// Interfejs dla bazy danych dinozaurów
2interface BazaDanychDinozaurów {
3 [id: string]: {
4 nazwa: string;
5 gatunek: string;
6 wiek: number;
7 zdrowie: "dobre" | "wymaga uwagi" | "krytyczne";
8 };
9}
10
11// Tworzenie bazy danych
12const bazaDanych: BazaDanychDinozaurów = {
13 "TREX-001": {
14 nazwa: "Rex",
15 gatunek: "Tyrannosaurus",
16 wiek: 7,
17 zdrowie: "dobre"
18 },
19 "VELOC-001": {
20 nazwa: "Blue",
21 gatunek: "Velociraptor",
22 wiek: 4,
23 zdrowie: "dobre"
24 },
25 "TRIC-003": {
26 nazwa: "Trike",
27 gatunek: "Triceratops",
28 wiek: 10,
29 zdrowie: "wymaga uwagi"
30 }
31};
32
33// Dostęp do danych
34console.log(bazaDanych["VELOC-001"].nazwa); // "Blue"
35
36// Dodawanie nowego wpisu
37bazaDanych["STEGO-002"] = {
38 nazwa: "Spike",
39 gatunek: "Stegosaurus",
40 wiek: 6,
41 zdrowie: "dobre"
42};Interfejsy mogą również definiować kontrakty, które klasy muszą implementować. To jak standardy operacyjne, które każdy departament w parku musi spełniać.
1// Interfejs dla systemów bezpieczeństwa
2interface SystemBezpieczeństwa {
3 nazwa: string;
4 czyAktywny: boolean;
5 poziomBezpieczeństwa: number;
6
7 aktywuj(): void;
8 dezaktywuj(): void;
9 sprawdźStan(): string;
10}
11
12// Klasa implementująca interfejs
13class SystemElektronicznychOgrodzeń implements SystemBezpieczeństwa {
14 nazwa: string;
15 czyAktywny: boolean;
16 poziomBezpieczeństwa: number;
17 napięcie: number; // Dodatkowa właściwość specyficzna dla tej klasy
18
19 constructor(nazwa: string, poziomBezpieczeństwa: number, napięcie: number) {
20 this.nazwa = nazwa;
21 this.czyAktywny = false; // Początkowo nieaktywny
22 this.poziomBezpieczeństwa = poziomBezpieczeństwa;
23 this.napięcie = napięcie;
24 }
25
26 aktywuj(): void {
27 this.czyAktywny = true;
28 console.log(`System ${this.nazwa} został aktywowany. Napięcie: ${this.napięcie}V`);
29 }
30
31 dezaktywuj(): void {
32 this.czyAktywny = false;
33 console.log(`System ${this.nazwa} został dezaktywowany.`);
34 }
35
36 sprawdźStan(): string {
37 return this.czyAktywny
38 ? `${this.nazwa}: AKTYWNY (Poziom ${this.poziomBezpieczeństwa})`
39 : `${this.nazwa}: NIEAKTYWNY`;
40 }
41
42 // Metoda specyficzna dla tej klasy
43 zwiększNapięcie(o: number): void {
44 this.napięcie += o;
45 console.log(`Zwiększono napięcie do ${this.napięcie}V`);
46 }
47}
48
49// Użycie klasy
50const ogrodzenieTRex = new SystemElektronicznychOgrodzeń("Ogrodzenie T-Rex", 5, 10000);
51ogrodzenieTRex.aktywuj();
52console.log(ogrodzenieTRex.sprawdźStan());
53ogrodzenieTRex.zwiększNapięcie(2000);Interfejsy mogą rozszerzać inne interfejsy, podobnie jak w biologii, gdzie gatunki dzielą wspólne cechy, ale mają też swoje unikalne właściwości.
1// Podstawowy interfejs dla wszystkich zwierząt w parku
2interface Zwierzę {
3 id: number;
4 gatunek: string;
5 wiek: number;
6 waga: number;
7}
8
9// Interfejs dla dinozaurów rozszerzający interfejs Zwierzę
10interface Dinozaur extends Zwierzę {
11 era: string; // np. "Kreda", "Jura"
12 jestDrapieżnikiem: boolean;
13 dietaGłówna: string[];
14}
15
16// Interfejs dla dinozaurów drapieżnych rozszerzający interfejs Dinozaur
17interface DrapieżnyDinozaur extends Dinozaur {
18 siłaUgryzienia: number; // w PSI
19 prędkośćBiegu: number; // w km/h
20 zachowaniePodczasPolowania: string[];
21}
22
23// Tworzenie obiektu zgodnego z interfejsem DrapieżnyDinozaur
24const velociraptor: DrapieżnyDinozaur = {
25 id: 101,
26 gatunek: "Velociraptor",
27 wiek: 4,
28 waga: 70,
29 era: "Kreda",
30 jestDrapieżnikiem: true,
31 dietaGłówna: ["małe ssaki", "jaszczurki", "inne małe dinozaury"],
32 siłaUgryzienia: 1000,
33 prędkośćBiegu: 60,
34 zachowaniePodczasPolowania: ["polowanie w stadzie", "zasadzka", "pościg"]
35};W TypeScript możliwe jest definiowanie tego samego interfejsu wielokrotnie - deklaracje zostaną połączone. To przydatne, gdy chcemy dodać nowe właściwości do istniejącego interfejsu.
1// Początkowa definicja protokołu bezpieczeństwa
2interface ProtokółBezpieczeństwa {
3 nazwa: string;
4 poziomZagrożenia: number;
5 proceduraEwakuacji: string;
6}
7
8// Rozszerzenie protokołu o dodatkowe właściwości
9interface ProtokółBezpieczeństwa {
10 personelOdpowiedzialny: string[];
11 czasReakcji: number; // w minutach
12}
13
14// Obiekt musi zawierać wszystkie właściwości z obu deklaracji
15const protokółAlfa: ProtokółBezpieczeństwa = {
16 nazwa: "Protokół Alfa",
17 poziomZagrożenia: 3,
18 proceduraEwakuacji: "Ewakuacja w kierunku głównego centrum kontroli",
19 personelOdpowiedzialny: ["Dr. Wu", "Owen Grady", "Claire Dearing"],
20 czasReakcji: 15
21};TypeScript oferuje również słowo kluczowe
type do definiowania typów. Choć często interfejsy i typy mogą być używane zamiennie, istnieją między nimi różnice:1// Definicja za pomocą interface
2interface PlanEwakuacji {
3 trasa: string[];
4 czasSzacunkowy: number;
5 punktyZbiórek: string[];
6}
7
8// Równoważna definicja za pomocą type
9type PlanEwakuacjiType = {
10 trasa: string[];
11 czasSzacunkowy: number;
12 punktyZbiórek: string[];
13};
14
15// Oba działają podobnie przy podstawowym użyciu
16const planA: PlanEwakuacji = {
17 trasa: ["Centrum operacyjne", "Main Street", "Hala ewakuacyjna"],
18 czasSzacunkowy: 25,
19 punktyZbiórek: ["Centrum operacyjne", "Hala ewakuacyjna"]
20};
21
22const planB: PlanEwakuacjiType = {
23 trasa: ["Wybieg Gallimimus", "Innovation Center", "Hala ewakuacyjna"],
24 czasSzacunkowy: 30,
25 punktyZbiórek: ["Innovation Center", "Hala ewakuacyjna"]
26};Rozszerzalność:
Złożone typy:
1// Możliwe tylko z typami
2type IDDinozaura = string | number;
3type KoordynatyWybiegu = [number, number, string]; // Tuple
4type StatusDinozaura = "aktywny" | "odpoczywający" | "śpiący"; // Union of literals
5
6// Bardziej naturalne z interfejsami
7interface Procedura {
8 nazwa: string;
9 kroki: string[];
10 czasWykonania: number;
11 personelOdpowiedzialny: string[];
12}Przyjrzyjmy się, jak możemy wykorzystać interfejsy do modelowania systemu zarządzania Parkiem Jurajskim:
1// Podstawowe interfejsy
2interface OsobaWParku {
3 id: string;
4 imię: string;
5 nazwisko: string;
6 poziomDostępu: 1 | 2 | 3 | 4 | 5;
7 dataZatrudnienia: Date;
8}
9
10interface Lokalizacja {
11 id: string;
12 nazwa: string;
13 typ: "wybieg" | "laboratorium" | "centrum operacyjne" | "strefa publiczna";
14 współrzędne: [number, number]; // [szerokość, długość]
15 wymaganyPoziomDostępu: 1 | 2 | 3 | 4 | 5;
16}
17
18interface GatunekDinozaura {
19 nazwa: string;
20 eraGeologiczna: string;
21 typ: "roślinożerca" | "drapieżnik" | "wszystkożerca";
22 przeciętnaWaga: number;
23 przeciętnyWiek: number;
24 poziomNiebezpieczeństwa: 1 | 2 | 3 | 4 | 5;
25 wymaganiaDiety: string[];
26 wymaganiaHabitatu: string[];
27}
28
29interface OkazDinozaura {
30 id: string;
31 gatunek: string;
32 imię?: string;
33 wiek: number;
34 waga: number;
35 płeć: "samiec" | "samica";
36 lokalizacja: string; // ID lokalizacji
37 stanZdrowia: "doskonały" | "dobry" | "wymaga uwagi" | "chory" | "krytyczny";
38 ostatnieBadanie: Date;
39 tagRFID: string;
40 notatki?: string;
41}
42
43// Interfejsy dla systemów zarządzania
44interface SystemŚledzeniaOkazów {
45 pobierzPozycję(idOkazu: string): [number, number];
46 śledźRuch(idOkazu: string, czasŚledzenia: number): [number, number][];
47 sprawdźCzyWStrefie(idOkazu: string, idStrefy: string): boolean;
48 alertGdyPozaStrefą(idOkazu: string, callback: (okaz: OkazDinozaura) => void): void;
49}
50
51interface SystemZarządzaniaPracownikami {
52 dodajPracownika(pracownik: OsobaWParku): void;
53 usuńPracownika(id: string): boolean;
54 przyznajDostęp(idPracownika: string, poziom: 1 | 2 | 3 | 4 | 5): boolean;
55 sprawdźObecnośćWStrefie(idPracownika: string, idStrefy: string): boolean;
56}
57
58// Przykład implementacji
59class SystemZarządzaniaParkiem {
60 private pracownicy: OsobaWParku[] = [];
61 private lokalizacje: Lokalizacja[] = [];
62 private gatunki: GatunekDinozaura[] = [];
63 private okazy: OkazDinozaura[] = [];
64
65 // Metody dla pracowników
66 dodajPracownika(pracownik: OsobaWParku): void {
67 this.pracownicy.push(pracownik);
68 console.log(`Dodano pracownika: ${pracownik.imię} ${pracownik.nazwisko}`);
69 }
70
71 // Metody dla lokalizacji
72 dodajLokalizację(lokalizacja: Lokalizacja): void {
73 this.lokalizacje.push(lokalizacja);
74 console.log(`Dodano lokalizację: ${lokalizacja.nazwa}`);
75 }
76
77 // Metody dla gatunków
78 dodajGatunek(gatunek: GatunekDinozaura): void {
79 this.gatunki.push(gatunek);
80 console.log(`Dodano gatunek: ${gatunek.nazwa}`);
81 }
82
83 // Metody dla okazów
84 dodajOkaz(okaz: OkazDinozaura): void {
85 this.okazy.push(okaz);
86 console.log(`Dodano okaz: ${okaz.id} (${okaz.gatunek})`);
87 }
88
89 // Metody zarządzania
90 sprawdźDostęp(idPracownika: string, idLokalizacji: string): boolean {
91 const pracownik = this.pracownicy.find(p => p.id === idPracownika);
92 const lokalizacja = this.lokalizacje.find(l => l.id === idLokalizacji);
93
94 if (!pracownik || !lokalizacja) return false;
95
96 return pracownik.poziomDostępu >= lokalizacja.wymaganyPoziomDostępu;
97 }
98
99 znajdźOkazyGatunku(nazwaGatunku: string): OkazDinozaura[] {
100 return this.okazy.filter(o => o.gatunek === nazwaGatunku);
101 }
102
103 znajdźOkazyWStrefie(idStrefy: string): OkazDinozaura[] {
104 return this.okazy.filter(o => o.lokalizacja === idStrefy);
105 }
106
107 generujRaportStanuParku(): {
108 liczbaPracowników: number;
109 liczbaLokalizacji: number;
110 liczbaGatunków: number;
111 liczbaOkazów: number;
112 stanZdrowiaOkazów: {
113 doskonały: number;
114 dobry: number;
115 wymagaUwagi: number;
116 chory: number;
117 krytyczny: number;
118 }
119 } {
120 // Analiza stanu zdrowia wszystkich okazów
121 const stanZdrowia = {
122 doskonały: 0,
123 dobry: 0,
124 wymagaUwagi: 0,
125 chory: 0,
126 krytyczny: 0
127 };
128
129 this.okazy.forEach(okaz => {
130 if (okaz.stanZdrowia === "doskonały") stanZdrowia.doskonały++;
131 else if (okaz.stanZdrowia === "dobry") stanZdrowia.dobry++;
132 else if (okaz.stanZdrowia === "wymaga uwagi") stanZdrowia.wymagaUwagi++;
133 else if (okaz.stanZdrowia === "chory") stanZdrowia.chory++;
134 else if (okaz.stanZdrowia === "krytyczny") stanZdrowia.krytyczny++;
135 });
136
137 return {
138 liczbaPracowników: this.pracownicy.length,
139 liczbaLokalizacji: this.lokalizacje.length,
140 liczbaGatunków: this.gatunki.length,
141 liczbaOkazów: this.okazy.length,
142 stanZdrowiaOkazów: stanZdrowia
143 };
144 }
145}
146
147// Przykład użycia
148const parkJurajski = new SystemZarządzaniaParkiem();
149
150// Dodanie lokalizacji
151parkJurajski.dodajLokalizację({
152 id: "LOC-001",
153 nazwa: "Wybieg T-Rex",
154 typ: "wybieg",
155 współrzędne: [23.45, -67.89],
156 wymaganyPoziomDostępu: 4
157});
158
159// Dodanie gatunku
160parkJurajski.dodajGatunek({
161 nazwa: "Tyrannosaurus Rex",
162 eraGeologiczna: "Kreda",
163 typ: "drapieżnik",
164 przeciętnaWaga: 7000,
165 przeciętnyWiek: 30,
166 poziomNiebezpieczeństwa: 5,
167 wymaganiaDiety: ["duże ilości mięsa", "uzupełnianie wapnia"],
168 wymaganiaHabitatu: ["duży obszar", "naturalna roślinność", "dostęp do wody"]
169});
170
171// Dodanie pracownika
172parkJurajski.dodajPracownika({
173 id: "EMP-001",
174 imię: "Owen",
175 nazwisko: "Grady",
176 poziomDostępu: 5,
177 dataZatrudnienia: new Date("2010-06-15")
178});
179
180// Dodanie okazu
181parkJurajski.dodajOkaz({
182 id: "DINO-001",
183 gatunek: "Tyrannosaurus Rex",
184 imię: "Rexy",
185 wiek: 7,
186 waga: 7500,
187 płeć: "samica",
188 lokalizacja: "LOC-001",
189 stanZdrowia: "dobry",
190 ostatnieBadanie: new Date("2023-03-20"),
191 tagRFID: "RFID-TREX-001"
192});
193
194// Generowanie raportu
195const raport = parkJurajski.generujRaportStanuParku();
196console.log("Raport stanu Parku Jurajskiego:");
197console.log(`Liczba pracowników: ${raport.liczbaPracowników}`);
198console.log(`Liczba lokalizacji: ${raport.liczbaLokalizacji}`);
199console.log(`Liczba gatunków: ${raport.liczbaGatunków}`);
200console.log(`Liczba okazów: ${raport.liczbaOkazów}`);
201console.log("Stan zdrowia okazów:", raport.stanZdrowiaOkazów);Interfejsy w TypeScript są potężnym narzędziem do modelowania struktur danych i definiowania kontraktów w kodzie. W kontekście zarządzania Parkiem Jurajskim, pozwalają nam precyzyjnie określić, jak powinny wyglądać różne systemy, procedury i elementy parku.
| Cecha interfejsu | Zastosowanie w Parku Jurajskim | |------------------|---------------------------------| | Podstawowa definicja | Struktury danych dla dinozaurów, pracowników, lokalizacji | | Właściwości opcjonalne | Dodatkowe informacje, które mogą, ale nie muszą być dostępne | | Właściwości readonly | Dane, które nie powinny być zmieniane, np. ID genetyczne | | Interfejsy funkcji | Definicje procedur bezpieczeństwa i protokołów | | Typy indeksowalne | Dynamiczne bazy danych dinozaurów i zasobów | | Interfejsy klas | Standardy dla systemów bezpieczeństwa i monitoringu | | Rozszerzanie interfejsów | Dziedziczenie cech między gatunkami dinozaurów | | Łączenie deklaracji | Rozszerzanie protokołów o dodatkowe procedury |
Dr. Wu podsumowuje: "Interfejsy w TypeScript, podobnie jak nasze protokoły w parku, zapewniają, że każdy element systemu działa zgodnie z oczekiwaniami. Dzięki nim możemy budować złożone systemy z pewnością, że poszczególne komponenty będą ze sobą współpracować - co jest absolutnie kluczowe, gdy mamy do czynienia z gatunkami, które mogą być niebezpieczne, jeśli coś pójdzie nie tak."