Witaj ponownie w najbardziej zaawansowanym laboratorium genetycznym Parku Jurajskiego! Dr. Henry Wu dziś zaprasza nas do zgłębienia jednego z najpotężniejszych mechanizmów TypeScript - funkcji generycznych. Podobnie jak zaawansowana inżynieria genetyczna pozwala nam tworzyć nowe gatunki dinozaurów z różnym DNA, funkcje generyczne pozwalają na tworzenie elastycznych, wielokrotnego użytku rozwiązań, które działają z różnymi typami danych, zachowując jednocześnie pełne bezpieczeństwo typów.
Funkcje generyczne to funkcje, które mogą pracować z różnymi typami danych, zachowując przy tym pełną kontrolę typów. Zamiast definiować konkretny typ parametru i zwracanej wartości, używamy parametrów typu (oznaczonych zwykle jako
T, U, K itp.), które są "wypełniane" konkretnymi typami w momencie wywołania funkcji.Wyobraź sobie, że zamiast tworzyć osobną funkcję dla każdego gatunku dinozaura, możesz stworzyć jedną generyczną funkcję, która będzie działała z każdym gatunkiem!
Przyjrzyjmy się, jak definiuje się i używa funkcji generycznych w TypeScript:
1// Prosta funkcja generyczna - zwraca ten sam element, który został przekazany
2function identycznośćGenetyczna<T>(obiekt: T): T {
3 return obiekt;
4}
5
6// Użycie z różnymi typami
7const łańcuchDNA: string = identycznośćGenetyczna<string>("ACGTACGT");
8const wiekTriceratopsa: number = identycznośćGenetyczna<number>(12);
9const aktywnyStatus: boolean = identycznośćGenetyczna<boolean>(true);
10
11// TypeScript często może wywnioskować typ generyczny na podstawie przekazanego argumentu
12const poziomAgresji = identycznośćGenetyczna(7); // TypeScript wnioskuje T jako numberW powyższym przykładzie:
identycznośćGenetyczna z parametrem typu TT i zwraca wartość tego samego typuFunkcje generyczne mogą mieć wiele parametrów typu, co pozwala na bardziej złożone operacje:
1// Funkcja generyczna z dwoma parametrami typu
2function parujDaneDinozaurów<T, U>(pierwsze: T, drugie: U): [T, U] {
3 return [pierwsze, drugie];
4}
5
6// Przykładowe użycie
7interface DanePodstawoweDinozaura {
8 nazwa: string;
9 gatunek: string;
10 wiek: number;
11}
12
13interface DaneZdrowotne {
14 temperaturaCiała: number;
15 tętno: number;
16 poziomEnergii: number;
17}
18
19const rexPodstawowe: DanePodstawoweDinozaura = {
20 nazwa: "Rex",
21 gatunek: "Tyrannosaurus",
22 wiek: 7
23};
24
25const rexZdrowie: DaneZdrowotne = {
26 temperaturaCiała: 38.2,
27 tętno: 76,
28 poziomEnergii: 8.5
29};
30
31// Łączenie różnych typów danych
32const pełneDaneRexa = parujDaneDinozaurów<DanePodstawoweDinozaura, DaneZdrowotne>(
33 rexPodstawowe,
34 rexZdrowie
35);
36
37console.log(`${pełneDaneRexa[0].nazwa} ma temperaturę ciała ${pełneDaneRexa[1].temperaturaCiała}°C`);Czasami chcemy, aby nasze funkcje generyczne działały tylko z określonymi typami danych, które spełniają pewne wymagania. Możemy to osiągnąć za pomocą ograniczeń generycznych poprzez słowo kluczowe
extends:1// Definiujemy interfejs opisujący minimalne wymagania
2interface PodstawoweInformacjeDinozaura {
3 id: string;
4 nazwa: string;
5 gatunek: string;
6}
7
8// Funkcja generyczna z ograniczeniem - T musi zawierać co najmniej właściwości z PodstawoweInformacjeDinozaura
9function wyświetlInformacjeDinozaura<T extends PodstawoweInformacjeDinozaura>(dino: T): void {
10 console.log(`ID: ${dino.id}`);
11 console.log(`Nazwa: ${dino.nazwa}`);
12 console.log(`Gatunek: ${dino.gatunek}`);
13
14 // Możemy również uzyskać dostęp do dodatkowych właściwości, które mogą być obecne w T
15 console.log("Dodatkowe informacje:");
16 const podstawoweKlucze = Object.keys({} as PodstawoweInformacjeDinozaura);
17 Object.entries(dino).forEach(([klucz, wartość]) => {
18 if (!podstawoweKlucze.includes(klucz)) {
19 console.log(`- ${klucz}: ${wartość}`);
20 }
21 });
22}
23
24// Przykład użycia z różnymi typami spełniającymi ograniczenie
25interface TRex extends PodstawoweInformacjeDinozaura {
26 siłaUgryzienia: number;
27 prędkośćBiegu: number;
28}
29
30const rex: TRex = {
31 id: "TREX-001",
32 nazwa: "Rexy",
33 gatunek: "Tyrannosaurus Rex",
34 siłaUgryzienia: 12800,
35 prędkośćBiegu: 32
36};
37
38interface Velociraptor extends PodstawoweInformacjeDinozaura {
39 inteligencja: number;
40 polowanieWGrupie: boolean;
41}
42
43const raptor: Velociraptor = {
44 id: "VELOC-001",
45 nazwa: "Blue",
46 gatunek: "Velociraptor",
47 inteligencja: 9,
48 polowanieWGrupie: true
49};
50
51// Obie funkcje zadziałają, ponieważ oba typy rozszerzają PodstawoweInformacjeDinozaura
52wyświetlInformacjeDinozaura(rex);
53wyświetlInformacjeDinozaura(raptor);
54
55// To nie zadziała, ponieważ brakuje wymaganych właściwości
56// wyświetlInformacjeDinozaura({ waga: 5000, wiek: 12 });W powyższym przykładzie:
PodstawoweInformacjeDinozaura z minimalnymi wymaganiamiSłowo kluczowe
keyof pozwala na ograniczenie parametru generycznego do kluczy innego typu. Jest to niezwykle przydatne przy tworzeniu funkcji, które operują na kluczach obiektów:1// Funkcja pobierająca wartość właściwości na podstawie jej klucza
2function pobierzWłaściwośćDinozaura<T, K extends keyof T>(obiekt: T, klucz: K): T[K] {
3 return obiekt[klucz];
4}
5
6// Przykładowe dane dinozaura
7interface DaneDinozaura {
8 id: string;
9 nazwa: string;
10 gatunek: string;
11 wiek: number;
12 waga: number;
13 drapieżnik: boolean;
14 cechy: string[];
15}
16
17const triceratops: DaneDinozaura = {
18 id: "TRIC-001",
19 nazwa: "Tricky",
20 gatunek: "Triceratops",
21 wiek: 15,
22 waga: 9000,
23 drapieżnik: false,
24 cechy: ["rogi", "tarcza na głowie", "silne nogi"]
25};
26
27// Użycie funkcji z różnymi kluczami
28const nazwaTriceratopsa = pobierzWłaściwośćDinozaura(triceratops, "nazwa");
29console.log(`Nazwa: ${nazwaTriceratopsa}`); // TypeScript wie, że to string
30
31const wiekTriceratopsa = pobierzWłaściwośćDinozaura(triceratops, "wiek");
32console.log(`Wiek: ${wiekTriceratopsa} lat`); // TypeScript wie, że to number
33
34const cechy = pobierzWłaściwośćDinozaura(triceratops, "cechy");
35console.log(`Cechy: ${cechy.join(", ")}`); // TypeScript wie, że to string[]
36
37// To nie zadziała, ponieważ "nieistniejącaWłaściwość" nie jest kluczem DaneDinozaura
38// pobierzWłaściwośćDinozaura(triceratops, "nieistniejącaWłaściwość");W tym przykładzie:
pobierzWłaściwośćDinozaura, która przyjmuje obiekt typu T i klucz K, ograniczony do kluczy TPodobnie jak funkcje mogą mieć wartości domyślne dla swoich parametrów, funkcje generyczne mogą mieć domyślne typy dla parametrów typu:
1// Funkcja generyczna z domyślnym parametrem typu
2function utworzRaportDinozaura<T = DaneDinozaura>(dane: T, etykieta: string = "Raport standardowy"): { dane: T, etykieta: string, timestamp: Date } {
3 return {
4 dane,
5 etykieta,
6 timestamp: new Date()
7 };
8}
9
10// Użycie z domyślnym typem
11const raportStandardowy = utworzRaportDinozaura(triceratops);
12console.log(`Raport: ${raportStandardowy.etykieta}`);
13console.log(`Dane dla: ${raportStandardowy.dane.nazwa}`);
14
15// Użycie z innym typem
16interface DaneKlimatyczne {
17 temperatura: number;
18 wilgotność: number;
19 ciśnienie: number;
20 lokalizacja: string;
21}
22
23const daneKlimatyczneWybiegu: DaneKlimatyczne = {
24 temperatura: 28.5,
25 wilgotność: 72,
26 ciśnienie: 1013,
27 lokalizacja: "Wybieg Triceratopsów"
28};
29
30const raportKlimatyczny = utworzRaportDinozaura<DaneKlimatyczne>(
31 daneKlimatyczneWybiegu,
32 "Raport klimatyczny"
33);
34
35console.log(`Raport: ${raportKlimatyczny.etykieta}`);
36console.log(`Temperatura: ${raportKlimatyczny.dane.temperatura}°C`);Funkcje generyczne są szczególnie przydatne przy pracy z tablicami i kolekcjami danych:
1// Funkcja generyczna do filtrowania tablicy
2function filtrujKolekcję<T>(kolekcja: T[], predykat: (element: T) => boolean): T[] {
3 return kolekcja.filter(predykat);
4}
5
6// Przykładowe dane dinozaurów
7interface KrótkaInformacjaDinozaura {
8 id: string;
9 nazwa: string;
10 gatunek: string;
11 drapieżnik: boolean;
12 waga: number;
13}
14
15const dinozaury: KrótkaInformacjaDinozaura[] = [
16 { id: "TREX-001", nazwa: "Rex", gatunek: "Tyrannosaurus", drapieżnik: true, waga: 8000 },
17 { id: "VEL-001", nazwa: "Blue", gatunek: "Velociraptor", drapieżnik: true, waga: 150 },
18 { id: "TRIC-001", nazwa: "Tricky", gatunek: "Triceratops", drapieżnik: false, waga: 9000 },
19 { id: "STEG-001", nazwa: "Spike", gatunek: "Stegosaurus", drapieżnik: false, waga: 5000 },
20 { id: "BRACH-001", nazwa: "Brachie", gatunek: "Brachiosaurus", drapieżnik: false, waga: 40000 }
21];
22
23// Filtrowanie dinozaurów drapieżnych
24const drapieżniki = filtrujKolekcję<KrótkaInformacjaDinozaura>(
25 dinozaury,
26 dino => dino.drapieżnik
27);
28
29console.log("Drapieżniki w parku:");
30drapieżniki.forEach(dino => console.log(`- ${dino.nazwa} (${dino.gatunek})`));
31
32// Filtrowanie dinozaurów cięższych niż 10000 kg
33const ciężkieDinozaury = filtrujKolekcję<KrótkaInformacjaDinozaura>(
34 dinozaury,
35 dino => dino.waga > 10000
36);
37
38console.log("
39Ciężkie dinozaury:");
40ciężkieDinozaury.forEach(dino => console.log(`- ${dino.nazwa}: ${dino.waga} kg`));
41
42// Bardziej zaawansowana funkcja generyczna do mapowania tablicy
43function przekształćKolekcję<T, U>(kolekcja: T[], transformacja: (element: T) => U): U[] {
44 return kolekcja.map(transformacja);
45}
46
47// Tworzenie prostszych obiektów na podstawie dinozaurów
48const etykietyDinozaurów = przekształćKolekcję<KrótkaInformacjaDinozaura, { nazwa: string, typ: string }>(
49 dinozaury,
50 dino => ({
51 nazwa: dino.nazwa,
52 typ: dino.drapieżnik ? "drapieżnik" : "roślinożerca"
53 })
54);
55
56console.log("
57Etykiety dinozaurów:");
58etykietyDinozaurów.forEach(etykieta => console.log(`- ${etykieta.nazwa}: ${etykieta.typ}`));Nie tylko funkcje mogą być generyczne - klasy i ich metody również mogą wykorzystywać tę potężną funkcjonalność:
1// Generyczna klasa Kolekcja - może przechowywać dane dowolnego typu
2class Kolekcja<T> {
3 private elementy: T[] = [];
4
5 // Metoda dodająca element do kolekcji
6 dodaj(element: T): void {
7 this.elementy.push(element);
8 }
9
10 // Metoda pobierająca element o danym indeksie
11 pobierz(indeks: number): T | undefined {
12 if (indeks >= 0 && indeks < this.elementy.length) {
13 return this.elementy[indeks];
14 }
15 return undefined;
16 }
17
18 // Metoda zwracająca wszystkie elementy
19 wszystkie(): T[] {
20 return [...this.elementy];
21 }
22
23 // Metoda filtrująca elementy
24 filtruj(predykat: (element: T) => boolean): T[] {
25 return this.elementy.filter(predykat);
26 }
27
28 // Generyczna metoda mapująca do innego typu
29 mapuj<U>(transformacja: (element: T) => U): U[] {
30 return this.elementy.map(transformacja);
31 }
32
33 // Metoda zwracająca liczbę elementów
34 liczbaElementów(): number {
35 return this.elementy.length;
36 }
37}
38
39// Przykład użycia z dinozaurami
40const kolekcjaDinozaurów = new Kolekcja<KrótkaInformacjaDinozaura>();
41
42// Dodawanie dinozaurów do kolekcji
43dinozaury.forEach(dino => kolekcjaDinozaurów.dodaj(dino));
44
45// Pobieranie konkretnego dinozaura
46const pierwszy = kolekcjaDinozaurów.pobierz(0);
47if (pierwszy) {
48 console.log(`Pierwszy dinozaur: ${pierwszy.nazwa} (${pierwszy.gatunek})`);
49}
50
51// Filtrowanie kolekcji
52const małeDinozaury = kolekcjaDinozaurów.filtruj(dino => dino.waga < 1000);
53console.log(`Liczba małych dinozaurów: ${małeDinozaury.length}`);
54
55// Mapowanie do raportów zdrowia
56interface RaportZdrowia {
57 id: string;
58 nazwa: string;
59 statusZdrowia: string;
60 uwagi: string;
61}
62
63const raportyZdrowia = kolekcjaDinozaurów.mapuj<RaportZdrowia>(dino => ({
64 id: dino.id,
65 nazwa: dino.nazwa,
66 statusZdrowia: "dobry", // Symulacja - w rzeczywistości pobieralibyśmy dane z bazy
67 uwagi: dino.drapieżnik
68 ? "Zachować szczególną ostrożność"
69 : "Standardowe procedury"
70}));
71
72console.log("
73Raporty zdrowia:");
74raportyZdrowia.forEach(raport => {
75 console.log(`- ${raport.nazwa}: ${raport.statusZdrowia} (${raport.uwagi})`);
76});Podobnie jak funkcje i klasy, również interfejsy i typy mogą być generyczne:
1// Generyczny interfejs dla systemu przechowywania
2interface System<T> {
3 dodaj(element: T): void;
4 pobierz(id: string): T | null;
5 aktualizuj(id: string, element: T): boolean;
6 usuń(id: string): boolean;
7 wszystkie(): T[];
8}
9
10// Implementacja generycznego systemu przechowywania dla dinozaurów
11class BazaDanychDinozaurów<T extends { id: string }> implements System<T> {
12 private dane: Map<string, T> = new Map();
13
14 dodaj(element: T): void {
15 this.dane.set(element.id, element);
16 }
17
18 pobierz(id: string): T | null {
19 return this.dane.has(id) ? this.dane.get(id)! : null;
20 }
21
22 aktualizuj(id: string, element: T): boolean {
23 if (this.dane.has(id)) {
24 this.dane.set(id, element);
25 return true;
26 }
27 return false;
28 }
29
30 usuń(id: string): boolean {
31 return this.dane.delete(id);
32 }
33
34 wszystkie(): T[] {
35 return Array.from(this.dane.values());
36 }
37}
38
39// Definiowanie typów z użyciem generycznego typu
40type DaneZPrzypisanym<T> = {
41 dane: T;
42 przypisanyPracownik: string;
43 ostatniaAktualizacja: Date;
44};
45
46// Użycie generycznego typu
47type DinozaurZOpiekunem = DaneZPrzypisanym<KrótkaInformacjaDinozaura>;
48
49// Tworzenie bazy danych dla dinozaurów z przypisanymi opiekunami
50const bazaDanychZOpiekunami = new BazaDanychDinozaurów<DinozaurZOpiekunem>();
51
52// Dodawanie rekordów
53bazaDanychZOpiekunami.dodaj({
54 dane: dinozaury[0],
55 przypisanyPracownik: "Owen Grady",
56 ostatniaAktualizacja: new Date(),
57 id: dinozaury[0].id
58});
59
60bazaDanychZOpiekunami.dodaj({
61 dane: dinozaury[1],
62 przypisanyPracownik: "Dr. Henry Wu",
63 ostatniaAktualizacja: new Date(),
64 id: dinozaury[1].id
65});
66
67// Pobieranie danych
68const rexZOpiekunem = bazaDanychZOpiekunami.pobierz("TREX-001");
69if (rexZOpiekunem) {
70 console.log(`
71Dinozaur ${rexZOpiekunem.dane.nazwa} jest pod opieką ${rexZOpiekunem.przypisanyPracownik}`);
72 console.log(`Ostatnia aktualizacja: ${rexZOpiekunem.ostatniaAktualizacja.toLocaleDateString()}`);
73}Przyjrzyjmy się kompleksowemu przykładowi, jak funkcje generyczne mogą być wykorzystane w systemie zarządzania Parkiem Jurajskim:
1// ===== SYSTEM ZARZĄDZANIA PARKIEM JURAJSKIM =====
2
3// Podstawowe interfejsy
4interface ElementParku {
5 id: string;
6 nazwa: string;
7 typElementu: string;
8}
9
10interface ObszarParku extends ElementParku {
11 typElementu: "obszar";
12 współrzędne: [number, number];
13 powierzchnia: number; // w hektarach
14 poziomBezpieczeństwa: 1 | 2 | 3 | 4 | 5;
15}
16
17interface WybiegDinozaurów extends ObszarParku {
18 typElementu: "wybieg";
19 mieszkańcy: string[]; // ID dinozaurów
20 typŚrodowiska: "las" | "sawanna" | "bagno" | "wybrzeże" | "akwen";
21 statusOgrodzenia: "aktywne" | "nieaktywne" | "w naprawie";
22}
23
24interface DinozaurParku extends ElementParku {
25 typElementu: "dinozaur";
26 gatunek: string;
27 wiek: number;
28 waga: number;
29 drapieżnik: boolean;
30 lokalizacja: string; // ID obszaru
31 statusZdrowia: "doskonały" | "dobry" | "wymaga uwagi" | "chory" | "krytyczny";
32}
33
34interface PracownikParku extends ElementParku {
35 typElementu: "pracownik";
36 stanowisko: string;
37 poziomDostępu: 1 | 2 | 3 | 4 | 5;
38 specjalizacja: string[];
39}
40
41// Generyczna klasa repozytorium
42class Repozytorium<T extends ElementParku> {
43 private elementy: Map<string, T> = new Map();
44
45 dodaj(element: T): void {
46 this.elementy.set(element.id, element);
47 }
48
49 pobierz(id: string): T | undefined {
50 return this.elementy.get(id);
51 }
52
53 wszystkie(): T[] {
54 return Array.from(this.elementy.values());
55 }
56
57 aktualizuj(id: string, dane: Partial<T>): boolean {
58 if (this.elementy.has(id)) {
59 const element = this.elementy.get(id)!;
60 this.elementy.set(id, { ...element, ...dane });
61 return true;
62 }
63 return false;
64 }
65
66 usuń(id: string): boolean {
67 return this.elementy.delete(id);
68 }
69
70 filtruj(predykat: (element: T) => boolean): T[] {
71 return this.wszystkie().filter(predykat);
72 }
73}
74
75// Generyczna funkcja do znajdowania powiązań między elementami
76function znajdźPowiązania<T extends ElementParku, U extends ElementParku>(
77 źródła: T[],
78 cele: U[],
79 predykat: (źródło: T, cel: U) => boolean
80): Array<[T, U[]]> {
81 return źródła.map(źródło => {
82 const powiązaneCele = cele.filter(cel => predykat(źródło, cel));
83 return [źródło, powiązaneCele] as [T, U[]];
84 });
85}
86
87// Generyczna funkcja do tworzenia raportów
88function generujRaport<T extends ElementParku>(
89 nazwa: string,
90 elementy: T[],
91 formatujElement: (element: T) => string
92): string {
93 let raport = `=== ${nazwa} ===
94`;
95 raport += `Data wygenerowania: ${new Date().toLocaleString()}
96`;
97 raport += `Liczba elementów: ${elementy.length}
98
99`;
100
101 if (elementy.length === 0) {
102 raport += "Brak danych do wyświetlenia.
103";
104 } else {
105 elementy.forEach(element => {
106 raport += formatujElement(element) + "
107";
108 });
109 }
110
111 return raport;
112}
113
114// Symulacja danych parku
115// Tworzenie repozytoriów
116const repoDinozaurów = new Repozytorium<DinozaurParku>();
117const repoObszarów = new Repozytorium<ObszarParku>();
118const repoWybiegów = new Repozytorium<WybiegDinozaurów>();
119const repoPracowników = new Repozytorium<PracownikParku>();
120
121// Dodawanie danych do repozytoriów
122// Obszary
123const obszarCentralny: ObszarParku = {
124 id: "OBS-001",
125 nazwa: "Obszar Centralny",
126 typElementu: "obszar",
127 współrzędne: [20.123, -75.456],
128 powierzchnia: 25,
129 poziomBezpieczeństwa: 1
130};
131repoObszarów.dodaj(obszarCentralny);
132
133// Wybiegi
134const wybiegTRex: WybiegDinozaurów = {
135 id: "WYB-001",
136 nazwa: "Wybieg T-Rex",
137 typElementu: "wybieg",
138 współrzędne: [20.145, -75.460],
139 powierzchnia: 8,
140 poziomBezpieczeństwa: 5,
141 mieszkańcy: ["DINO-001"],
142 typŚrodowiska: "las",
143 statusOgrodzenia: "aktywne"
144};
145repoWybiegów.dodaj(wybiegTRex);
146
147const wybiegRaptory: WybiegDinozaurów = {
148 id: "WYB-002",
149 nazwa: "Wybieg Raptorów",
150 typElementu: "wybieg",
151 współrzędne: [20.150, -75.465],
152 powierzchnia: 5,
153 poziomBezpieczeństwa: 5,
154 mieszkańcy: ["DINO-002", "DINO-003", "DINO-004", "DINO-005"],
155 typŚrodowiska: "sawanna",
156 statusOgrodzenia: "aktywne"
157};
158repoWybiegów.dodaj(wybiegRaptory);
159
160// Dinozaury
161const trex: DinozaurParku = {
162 id: "DINO-001",
163 nazwa: "Rexy",
164 typElementu: "dinozaur",
165 gatunek: "Tyrannosaurus Rex",
166 wiek: 7,
167 waga: 8000,
168 drapieżnik: true,
169 lokalizacja: "WYB-001",
170 statusZdrowia: "dobry"
171};
172repoDinozaurów.dodaj(trex);
173
174const raptor1: DinozaurParku = {
175 id: "DINO-002",
176 nazwa: "Blue",
177 typElementu: "dinozaur",
178 gatunek: "Velociraptor",
179 wiek: 4,
180 waga: 150,
181 drapieżnik: true,
182 lokalizacja: "WYB-002",
183 statusZdrowia: "doskonały"
184};
185repoDinozaurów.dodaj(raptor1);
186
187// Pracownicy
188const trener: PracownikParku = {
189 id: "PRAC-001",
190 nazwa: "Owen Grady",
191 typElementu: "pracownik",
192 stanowisko: "Treser Raptorów",
193 poziomDostępu: 4,
194 specjalizacja: ["Behawioryzm", "Tresura dinozaurów", "Operacje terenowe"]
195};
196repoPracowników.dodaj(trener);
197
198const naukowiec: PracownikParku = {
199 id: "PRAC-002",
200 nazwa: "Dr. Henry Wu",
201 typElementu: "pracownik",
202 stanowisko: "Główny Genetyk",
203 poziomDostępu: 5,
204 specjalizacja: ["Genetyka", "Inżynieria genetyczna", "Klonowanie"]
205};
206repoPracowników.dodaj(naukowiec);
207
208// Przykładowe użycie generycznych funkcji
209
210// 1. Znajdowanie dinozaurów w określonych wybiegach
211const dinozauryWWybiegach = znajdźPowiązania<WybiegDinozaurów, DinozaurParku>(
212 repoWybiegów.wszystkie(),
213 repoDinozaurów.wszystkie(),
214 (wybieg, dino) => wybieg.mieszkańcy.includes(dino.id)
215);
216
217// 2. Generowanie raportu o stanie wybiegów
218const raportWybiegów = generujRaport<WybiegDinozaurów>(
219 "RAPORT STANU WYBIEGÓW",
220 repoWybiegów.wszystkie(),
221 wybieg => {
222 const liczbaMieszkańców = wybieg.mieszkańcy.length;
223 return `${wybieg.nazwa} (ID: ${wybieg.id})
224 - Typ środowiska: ${wybieg.typŚrodowiska}
225 - Powierzchnia: ${wybieg.powierzchnia} ha
226 - Status ogrodzenia: ${wybieg.statusOgrodzenia}
227 - Poziom bezpieczeństwa: ${wybieg.poziomBezpieczeństwa}
228 - Liczba mieszkańców: ${liczbaMieszkańców}`;
229 }
230);
231
232// 3. Generowanie raportu o stanie zdrowia dinozaurów
233const raportZdrowiaDinozaurów = generujRaport<DinozaurParku>(
234 "RAPORT ZDROWIA DINOZAURÓW",
235 repoDinozaurów.wszystkie(),
236 dino => {
237 const lokalizacja = repoWybiegów.pobierz(dino.lokalizacja)?.nazwa || "Nieznana";
238 return `${dino.nazwa} (${dino.gatunek})
239 - ID: ${dino.id}
240 - Wiek: ${dino.wiek} lat
241 - Waga: ${dino.waga} kg
242 - Status zdrowia: ${dino.statusZdrowia}
243 - Lokalizacja: ${lokalizacja}`;
244 }
245);
246
247// Wyświetlanie raportów
248console.log("=== DINOZAURY W WYBIEGACH ===");
249dinozauryWWybiegach.forEach(([wybieg, dinozaury]) => {
250 console.log(`Wybieg: ${wybieg.nazwa}`);
251 if (dinozaury.length === 0) {
252 console.log(" Brak mieszkańców");
253 } else {
254 dinozaury.forEach(dino => {
255 console.log(` - ${dino.nazwa} (${dino.gatunek}): ${dino.statusZdrowia}`);
256 });
257 }
258});
259
260console.log("
261" + raportWybiegów);
262console.log("
263" + raportZdrowiaDinozaurów);Funkcje generyczne to niezwykle potężne narzędzie w TypeScript, które pozwala na tworzenie elastycznych, wielokrotnego użytku komponentów, zachowując jednocześnie pełne bezpieczeństwo typów. Podobnie jak zaawansowane techniki genetyczne Dr. Wu, funkcje generyczne pozwalają na tworzenie wysoce adaptacyjnych rozwiązań, które mogą działać z różnymi typami danych.
| Technika | Zastosowanie w Parku Jurajskim | |----------|----------------------------------| | Podstawowe funkcje generyczne | Obsługa różnych typów danych dinozaurów | | Wieloparametrowe funkcje generyczne | Łączenie danych o dinozaurach z różnych źródeł | | Ograniczenia generyczne | Zapewnienie, że funkcje działają tylko z odpowiednimi typami dinozaurów | | Ograniczenia z
keyof | Bezpieczny dostęp do właściwości dinozaurów |
| Domyślne parametry typu | Uproszczenie pracy z najczęściej używanymi typami danych |
| Generyczne operacje na kolekcjach | Efektywne zarządzanie grupami dinozaurów |
| Generyczne klasy i metody | Tworzenie elastycznych systemów zarządzania okazami |
| Generyczne interfejsy i typy | Definiowanie spójnych kontraktów dla różnych podsystemów parku |Dr. Wu podsumowuje: "Tak jak nasze przełomowe techniki genetyczne pozwoliły nam przywrócić do życia gatunki, które zniknęły z Ziemi 65 milionów lat temu, tak generyczne funkcje TypeScript pozwalają programistom tworzyć elastyczne, adaptacyjne rozwiązania, które mogą ewoluować wraz z potrzebami systemu. Pamiętaj jednak, że z wielką mocą wiąże się wielka odpowiedzialność - zarówno w inżynierii genetycznej, jak i w programowaniu."