Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Typy union i intersection

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

Union Types - czyli "albo to, albo to"

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.

Podstawowa składnia typów union

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 | number

Praktyczne zastosowania typów union

Typy union są niezwykle przydatne w wielu praktycznych scenariuszach w Parku Jurajskim:

1. Obsługa różnych formatów identyfikatorów

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"

2. Funkcje zwracające różne typy

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}

3. Obsługa opcjonalnych parametrów

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 filtrami

Discriminated Unions (Unie dyskryminacyjne)

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

Intersection Types - czyli "to i to"

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

Podstawowa składnia typów intersection

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};

Praktyczne zastosowania typów intersection

Typy intersection są szczególnie przydatne w budowaniu złożonych systemów w Parku Jurajskim:

1. Kompozycja zachowań

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};

2. Rozszerzanie istniejących typów

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};

3. Łączenie funkcjonalności w złożonych systemach

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

Połączenie union i intersection - zaawansowane przypadki

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

Podsumowanie

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

Vai a CodeWorlds