Witaj w zaawansowanym laboratorium typów TypeScript w Parku Jurajskim! Dr. Henry Wu jest dziś szczególnie podekscytowany, ponieważ będziemy omawiać dwa potężne mechanizmy TypeScript, które można porównać do jego najbardziej ambitnych projektów inżynierii genetycznej: typy union (unii) i intersection (przecięcia).
Typ union (unii) pozwala zmiennej przyjmować wartości jednego z kilku różnych typów. To trochę jak wybieg hybrydowy w parku, który może pomieścić różne gatunki dinozaurów, ale tylko jeden rodzaj na raz.
Typ union tworzymy za pomocą operatora
| (pionowa kreska):1// Zmienna może być typu string LUB number
2let identyfikatorDinozaura: string | number;
3
4identyfikatorDinozaura = "TREX-01"; // Ok, string jest dozwolony
5identyfikatorDinozaura = 12345; // Ok, number jest dozwolony
6// identyfikatorDinozaura = true; // Błąd! boolean nie jest częścią unii string | numberTypy union są niezwykle przydatne w wielu praktycznych scenariuszach w Parku Jurajskim:
1// Funkcja, która może przyjąć różne typy identyfikatorów
2function znajdźDinozaura(id: string | number): void {
3 // Musimy sprawdzić typ, zanim wykonamy operacje specyficzne dla danego typu
4 if (typeof id === "string") {
5 console.log(`Szukam dinozaura o kodzie: ${id.toUpperCase()}`);
6 } else {
7 console.log(`Szukam dinozaura o numerze: ${id.toFixed(0)}`);
8 }
9}
10
11znajdźDinozaura("RAPTOR-BLUE"); // "Szukam dinozaura o kodzie: RAPTOR-BLUE"
12znajdźDinozaura(42); // "Szukam dinozaura o numerze: 42"1// Funkcja, która może zwrócić dinozaura lub komunikat o błędzie
2function pobierzDaneDinozaura(id: string):
3 { nazwa: string; gatunek: string; wiek: number } | string {
4
5 // Symulacja bazy danych
6 const bazaDanych = new Map([
7 ["TRex1", { nazwa: "Rex", gatunek: "Tyrannosaurus", wiek: 7 }],
8 ["Rap1", { nazwa: "Blue", gatunek: "Velociraptor", wiek: 4 }]
9 ]);
10
11 const dinozaur = bazaDanych.get(id);
12
13 if (dinozaur) {
14 return dinozaur; // Zwraca obiekt dinozaura
15 } else {
16 return `Nie znaleziono dinozaura o ID: ${id}`; // Zwraca komunikat błędu
17 }
18}
19
20const wynik = pobierzDaneDinozaura("TRex1");
21// Musimy sprawdzić typ zwróconej wartości
22if (typeof wynik === "string") {
23 console.log(`Błąd: ${wynik}`);
24} else {
25 console.log(`Znaleziono: ${wynik.nazwa}, gatunek: ${wynik.gatunek}`);
26}1// Funkcja tworząca raport, która może przyjąć różne parametry filtrowania
2function generujRaportDinozaurów(
3 filtr?: { gatunek?: string; minWiek?: number; drapieżniki?: boolean }
4): string {
5 let raport = "Raport dinozaurów:
6";
7
8 if (filtr) {
9 raport += "Filtry:";
10 if (filtr.gatunek) raport += ` Gatunek=${filtr.gatunek}`;
11 if (filtr.minWiek) raport += ` MinWiek=${filtr.minWiek}`;
12 if (filtr.drapieżniki !== undefined) raport += ` TylkoDrapieżniki=${filtr.drapieżniki}`;
13 } else {
14 raport += "Brak filtrów - pokazuję wszystkie dinozaury";
15 }
16
17 return raport;
18}
19
20console.log(generujRaportDinozaurów()); // Bez filtrów
21console.log(generujRaportDinozaurów({ gatunek: "Velociraptor" })); // Z filtrem gatunku
22console.log(generujRaportDinozaurów({ minWiek: 5, drapieżniki: true })); // Z wieloma filtramiUnie dyskryminacyjne to zaawansowana technika używania typów union z "właściwością dyskryminującą", która pozwala TypeScriptowi rozpoznać, z którym typem mamy do czynienia. To jak system identyfikacji dinozaurów w parku, który pozwala natychmiast rozpoznać, czy mamy do czynienia z roślinożercą czy drapieżnikiem.
1// Definiowanie interfejsów z właściwością dyskryminującą "typ"
2interface Roślinożerca {
3 typ: "roślinożerca";
4 dieta: string[];
5 poziomAgresji: number; // 1-10
6}
7
8interface Drapieżnik {
9 typ: "drapieżnik";
10 ofiary: string[];
11 siłaUgryzienia: number; // w PSI
12}
13
14// Typ będący unią dwóch interfejsów
15type Dinozaur = Roślinożerca | Drapieżnik;
16
17// Funkcja korzystająca z unii dyskryminacyjnej
18function zarządzajDinozaurem(dino: Dinozaur): void {
19 // Sprawdzamy właściwość dyskryminującą
20 switch (dino.typ) {
21 case "roślinożerca":
22 // TypeScript wie, że tutaj dino jest typu Roślinożerca
23 console.log(`Przygotuj paszę: ${dino.dieta.join(", ")}`);
24 if (dino.poziomAgresji > 7) {
25 console.log("Uwaga: Roślinożerca z wysokim poziomem agresji!");
26 }
27 break;
28 case "drapieżnik":
29 // TypeScript wie, że tutaj dino jest typu Drapieżnik
30 console.log(`Przygotuj mięso dla: ${dino.ofiary.join(", ")}`);
31 if (dino.siłaUgryzienia > 10000) {
32 console.log("Uwaga: Bardzo niebezpieczny drapieżnik!");
33 }
34 break;
35 }
36}
37
38// Przykłady użycia
39const triceratops: Roślinożerca = {
40 typ: "roślinożerca",
41 dieta: ["paprocie", "cykady", "iglaste"],
42 poziomAgresji: 6
43};
44
45const tRex: Drapieżnik = {
46 typ: "drapieżnik",
47 ofiary: ["triceratops", "edmontosaurus", "pachycephalosaurus"],
48 siłaUgryzienia: 12800
49};
50
51zarządzajDinozaurem(triceratops);
52zarządzajDinozaurem(tRex);Typ intersection (przecięcia) pozwala na połączenie wielu typów w jeden, bardziej złożony typ. To jak eksperymenty Dr. Wu z łączeniem DNA różnych dinozaurów, by stworzyć zupełnie nowe gatunki o cechach wszystkich "składników".
Typ intersection tworzymy za pomocą operatora
&:1// Definiowanie interfejsów podstawowych cech
2interface PodstawowyDinozaur {
3 id: number;
4 nazwa: string;
5 gatunek: string;
6 wiek: number;
7}
8
9interface CechyFizyczne {
10 wysokość: number; // w metrach
11 długość: number; // w metrach
12 waga: number; // w kilogramach
13}
14
15interface CechyBehawioralne {
16 poziomAgresji: number; // 1-10
17 terytorialny: boolean;
18 społeczny: boolean;
19}
20
21// Tworzenie typu będącego przecięciem trzech interfejsów
22type PełnyProfilDinozaura = PodstawowyDinozaur & CechyFizyczne & CechyBehawioralne;
23
24// Teraz obiekt musi implementować wszystkie właściwości ze wszystkich trzech interfejsów
25const velociraptor: PełnyProfilDinozaura = {
26 // Z PodstawowyDinozaur
27 id: 101,
28 nazwa: "Blue",
29 gatunek: "Velociraptor",
30 wiek: 4,
31
32 // Z CechyFizyczne
33 wysokość: 0.5,
34 długość: 2,
35 waga: 15,
36
37 // Z CechyBehawioralne
38 poziomAgresji: 8,
39 terytorialny: true,
40 społeczny: true
41};Typy intersection są szczególnie przydatne w budowaniu złożonych systemów w Parku Jurajskim:
1// Definiowanie różnych umiejętności
2interface Pływający {
3 prędkośćPływania: number; // km/h
4 maksGłębokość: number; // metry
5 wytrzymałośćPodWodą: number; // minuty
6}
7
8interface Latający {
9 rozpiętośćSkrzydeł: number; // metry
10 prędkośćLotu: number; // km/h
11 maksWysokość: number; // metry
12}
13
14// Dinozaur, który potrafi i pływać, i latać (jak Pteranodon)
15type LatającyIPływającyDinozaur = PodstawowyDinozaur & Latający & Pływający;
16
17const pteranodon: LatającyIPływającyDinozaur = {
18 id: 201,
19 nazwa: "Pterri",
20 gatunek: "Pteranodon",
21 wiek: 12,
22
23 // Umiejętności latania
24 rozpiętośćSkrzydeł: 8,
25 prędkośćLotu: 90,
26 maksWysokość: 500,
27
28 // Umiejętności pływania
29 prędkośćPływania: 20,
30 maksGłębokość: 10,
31 wytrzymałośćPodWodą: 3
32};1// Podstawowy interfejs bezpieczeństwa dla wszystkich wybiegów
2interface PodstawowySystemBezpieczeństwa {
3 podwójneOgrodzenie: boolean;
4 monitoringWideo: boolean;
5 czujnikiRuchu: boolean;
6}
7
8// Dodatkowe środki bezpieczeństwa dla drapieżników
9interface ZabezpieczeniaDlaDrapieżników {
10 napięcieElektryczne: number; // wolty
11 grubośćŚcian: number; // cm
12 kopułaZabezpieczająca: boolean;
13}
14
15// Pełny system bezpieczeństwa dla wybiegu drapieżników
16type WybiegDrapieżnika = PodstawowySystemBezpieczeństwa & ZabezpieczeniaDlaDrapieżników;
17
18// Używamy intersection do dodania informacji o paddle
19type WybiegTRex = WybiegDrapieżnika & {
20 obszar: number; // hektary
21 ukształtowanieTerenu: string[];
22 roślinnośćWiarygodna: boolean;
23};
24
25const wybiegRex: WybiegTRex = {
26 // PodstawowySystemBezpieczeństwa
27 podwójneOgrodzenie: true,
28 monitoringWideo: true,
29 czujnikiRuchu: true,
30
31 // ZabezpieczeniaDlaDrapieżników
32 napięcieElektryczne: 10000,
33 grubośćŚcian: 50,
34 kopułaZabezpieczająca: false,
35
36 // Specyficzne dla WybiegTRex
37 obszar: 8,
38 ukształtowanieTerenu: ["wysoka trawa", "wzgórza", "strumień"],
39 roślinnośćWiarygodna: true
40};1// Interfejs systemu obserwacji
2interface SystemObserwacji {
3 kamery: number;
4 rozdzielczość: string;
5 śledzeniePodczerwienią: boolean;
6
7 rozpocznijNagrywanie(): void;
8 zatrzymajNagrywanie(): void;
9}
10
11// Interfejs systemu alarmowego
12interface SystemAlarmowy {
13 sygnałDźwiękowy: boolean;
14 sygnałŚwietlny: boolean;
15 powiadomienieSMS: boolean;
16
17 uruchomAlarm(poziom: number): void;
18 wyłączAlarm(): void;
19}
20
21// Złożony system bezpieczeństwa łączący obie funkcjonalności
22type SystemBezpieczeństwaParku = SystemObserwacji & SystemAlarmowy;
23
24// Implementacja systemu bezpieczeństwa
25class PełnySystemBezpieczeństwa implements SystemBezpieczeństwaParku {
26 // SystemObserwacji
27 kamery: number = 24;
28 rozdzielczość: string = "4K";
29 śledzeniePodczerwienią: boolean = true;
30
31 rozpocznijNagrywanie(): void {
32 console.log("Rozpoczynam nagrywanie ze wszystkich kamer");
33 }
34
35 zatrzymajNagrywanie(): void {
36 console.log("Zatrzymuję nagrywanie");
37 }
38
39 // SystemAlarmowy
40 sygnałDźwiękowy: boolean = true;
41 sygnałŚwietlny: boolean = true;
42 powiadomienieSMS: boolean = true;
43
44 uruchomAlarm(poziom: number): void {
45 console.log(`Uruchamiam alarm poziomu ${poziom}!`);
46 if (this.sygnałDźwiękowy) console.log("Syreny włączone");
47 if (this.sygnałŚwietlny) console.log("Światła ostrzegawcze włączone");
48 if (this.powiadomienieSMS) console.log("Wysyłam powiadomienia SMS do personelu");
49 }
50
51 wyłączAlarm(): void {
52 console.log("Wyłączam wszystkie systemy alarmowe");
53 }
54
55 // Dodatkowe metody specyficzne dla pełnego systemu
56 uruchomProcedurębezpieczeństwa(kodZagrożenia: string): void {
57 console.log(`Inicjuję procedurę bezpieczeństwa: ${kodZagrożenia}`);
58 this.rozpocznijNagrywanie();
59 this.uruchomAlarm(kodZagrożenia === "CZERWONY" ? 3 : 1);
60 }
61}
62
63// Użycie systemu
64const systemBezpieczeństwa = new PełnySystemBezpieczeństwa();
65systemBezpieczeństwa.uruchomProcedurębezpieczeństwa("CZERWONY");Prawdziwa potęga TypeScript ujawnia się, gdy łączymy typy union i intersection w bardziej złożone struktury - podobnie jak Dr. Wu łączy DNA różnych gatunków, aby stworzyć zupełnie nowe dinozaury.
1// Podstawowe typy dinozaurów
2interface Dinozaur {
3 id: number;
4 nazwa: string;
5 gatunek: string;
6}
7
8interface LądowyDinozaur {
9 środowisko: "lądowe";
10 prędkośćBiegu: number; // km/h
11}
12
13interface WodnyDinozaur {
14 środowisko: "wodne";
15 prędkośćPływania: number; // km/h
16 maksGłębokość: number; // metry
17}
18
19interface PowietrznyDinozaur {
20 środowisko: "powietrzne";
21 prędkośćLotu: number; // km/h
22 maksWysokość: number; // metry
23}
24
25// Typy dla różnych zdolności
26interface ZdolnościŁowieckie {
27 siłaUgryzienia: number; // PSI
28 strategiaPolowania: "zasadzka" | "pościg" | "stadna";
29}
30
31interface ZdolnościObronneRoślinożercy {
32 pancerz: boolean;
33 rogi: boolean;
34 kolce: boolean;
35 siłaObronna: number; // 1-10
36}
37
38// Złożony typ wykorzystujący union i intersection
39type DinozaurParkowy = Dinozaur &
40 (
41 (LądowyDinozaur & ZdolnościŁowieckie) |
42 (LądowyDinozaur & ZdolnościObronneRoślinożercy) |
43 (WodnyDinozaur & ZdolnościŁowieckie) |
44 (PowietrznyDinozaur & ZdolnościŁowieckie)
45 );
46
47// Przykłady użycia
48const tyranozaur: DinozaurParkowy = {
49 id: 1,
50 nazwa: "Rex",
51 gatunek: "Tyrannosaurus Rex",
52 środowisko: "lądowe",
53 prędkośćBiegu: 30,
54 siłaUgryzienia: 12800,
55 strategiaPolowania: "pościg"
56};
57
58const stegozaur: DinozaurParkowy = {
59 id: 2,
60 nazwa: "Spike",
61 gatunek: "Stegosaurus",
62 środowisko: "lądowe",
63 prędkośćBiegu: 7,
64 pancerz: true,
65 rogi: false,
66 kolce: true,
67 siłaObronna: 8
68};
69
70// Funkcja obsługująca złożone typy
71function analizujDinozaura(dino: DinozaurParkowy): void {
72 console.log(`Analizuję: ${dino.nazwa} (${dino.gatunek})`);
73
74 // Sprawdzamy środowisko
75 if (dino.środowisko === "lądowe") {
76 console.log(`Prędkość biegu: ${dino.prędkośćBiegu} km/h`);
77
78 // Sprawdzamy, czy to drapieżnik czy roślinożerca
79 if ("siłaUgryzienia" in dino) {
80 console.log(`Drapieżnik - siła ugryzienia: ${dino.siłaUgryzienia} PSI`);
81 console.log(`Strategia polowania: ${dino.strategiaPolowania}`);
82 } else if ("siłaObronna" in dino) {
83 console.log("Roślinożerca z mechanizmami obronnymi:");
84 if (dino.pancerz) console.log("- Wyposażony w pancerz");
85 if (dino.rogi) console.log("- Posiada rogi");
86 if (dino.kolce) console.log("- Posiada kolce");
87 console.log(`Siła obronna: ${dino.siłaObronna}/10`);
88 }
89 } else if (dino.środowisko === "wodne") {
90 console.log(`Wodny drapieżnik - prędkość pływania: ${dino.prędkośćPływania} km/h`);
91 console.log(`Maksymalna głębokość: ${dino.maksGłębokość} m`);
92 } else if (dino.środowisko === "powietrzne") {
93 console.log(`Powietrzny drapieżnik - prędkość lotu: ${dino.prędkośćLotu} km/h`);
94 console.log(`Maksymalna wysokość lotu: ${dino.maksWysokość} m`);
95 }
96}
97
98analizujDinozaura(tyranozaur);
99console.log("---");
100analizujDinozaura(stegozaur);Typy union i intersection są jak zaawansowane techniki inżynierii genetycznej Dr. Wu - dają nam niezwykłą moc i elastyczność w projektowaniu złożonych systemów TypeScript.
| Typ | Operator | Analogia w Parku Jurajskim | Zastosowanie | |-----|----------|----------------------------|--------------| | Union |
| | Wybieg mogący pomieścić różne dinozaury (ale tylko jeden rodzaj jednocześnie) | Zmienne, które mogą mieć różne typy |
| Discriminated Union | | z właściwością dyskryminującą | System identyfikacji różnych gatunków dinozaurów | Bezpieczna obsługa różnych wariantów typu |
| Intersection | & | Łączenie DNA z różnych dinozaurów | Łączenie interfejsów/typów w bardziej złożone struktury |Dr. Wu podsumowuje dzisiejszą lekcję: "TypeScript, podobnie jak nasze laboratorium genetyczne, daje nam ogromne możliwości. Dzięki union i intersection types możemy tworzyć systemy dokładnie dopasowane do naszych potrzeb, łącząc proste elementy w złożone struktury. Jednak pamiętaj - z wielką mocą przychodzi wielka odpowiedzialność. Użyj tej wiedzy mądrze, aby stworzyć bezpieczny i niezawodny park."