"Nadszedł czas na podsumowanie," ogłasza Dr. Henry Wu, główny genetyk InGen, stojąc przed ogromnym holoekranem laboratorium. "Przez ostatnie tygodnie opanowałeś zaawansowane mechanizmy TypeScript - od modyfikatorów dostępu, przez generyki, po typy warunkowe i mapowane. Teraz zbudujesz kompletny system zarządzania Parkiem Jurajskim, który połączy wszystkie te elementy w jedną, spójną całość."
W tym projekcie stworzysz zaawansowany system typów dla Parku Jurajskiego. Wykorzystasz klasy abstrakcyjne, interfejsy, generyki, typy warunkowe, typy mapowane, utility types i dekoratory - wszystko to, czego nauczyłeś się w tym module. System będzie obsługiwał rejestrację dinozaurów, zarządzanie zagrodami, monitorowanie stanu zdrowia i generowanie raportów bezpieczeństwa.
Zbudujesz kompletny, w pełni typowany system
JurassicParkManager, który:Zdefiniuj abstrakcyjną klasę bazową
Dinosaur z właściwościami chronionymi i abstrakcyjnymi metodami. Następnie stwórz interfejsy opisujące różne zdolności dinozaurów i zaimplementuj je w klasach potomnych.1// Interfejsy zdolności
2interface IHerbivore {
3 graze(area: string): string;
4 preferredPlants: string[];
5}
6
7interface ICarnivore {
8 hunt(target: string): string;
9 attackPower: number;
10}
11
12interface IFlying {
13 fly(altitude: number): string;
14 maxAltitude: number;
15}
16
17interface ISwimming {
18 swim(depth: number): string;
19 maxDepth: number;
20}
21
22// Klasa abstrakcyjna z modyfikatorami dostępu
23abstract class Dinosaur {
24 private static nextId: number = 1;
25
26 protected readonly id: number;
27 public readonly species: string;
28 protected health: number;
29 private enclosureId: string | null = null;
30
31 constructor(species: string, health: number = 100) {
32 this.id = Dinosaur.nextId++;
33 this.species = species;
34 this.health = health;
35 }
36
37 // Metoda abstrakcyjna - każdy podtyp definiuje swój poziom zagrożenia
38 abstract get dangerLevel(): 'low' | 'medium' | 'high' | 'critical';
39
40 // Metoda abstrakcyjna - opis zachowania
41 abstract describeBehavior(): string;
42
43 // Statyczna metoda fabrykująca
44 static getNextId(): number {
45 return Dinosaur.nextId;
46 }
47
48 // Publiczne API
49 public getStatus(): string {
50 return `[${this.id}] ${this.species} - Zdrowie: ${this.health}%`;
51 }
52
53 public assignEnclosure(enclosureId: string): void {
54 this.enclosureId = enclosureId;
55 }
56
57 protected heal(amount: number): void {
58 this.health = Math.min(100, this.health + amount);
59 }
60}
61
62// Klasa potomna implementująca interfejsy
63class TyrannosaurusRex extends Dinosaur implements ICarnivore {
64 public attackPower: number = 95;
65
66 constructor() {
67 super('Tyrannosaurus Rex', 100);
68 }
69
70 get dangerLevel(): 'critical' {
71 return 'critical';
72 }
73
74 describeBehavior(): string {
75 return 'Drapieżnik szczytowy - wymaga maksymalnych zabezpieczeń';
76 }
77
78 hunt(target: string): string {
79 return `T-Rex poluje na ${target} z siłą ataku ${this.attackPower}`;
80 }
81}
82
83class Pteranodon extends Dinosaur implements ICarnivore, IFlying {
84 public attackPower: number = 40;
85 public maxAltitude: number = 500;
86
87 constructor() {
88 super('Pteranodon', 100);
89 }
90
91 get dangerLevel(): 'high' {
92 return 'high';
93 }
94
95 describeBehavior(): string {
96 return 'Latający drapieżnik - wymaga zadaszonej zagrody';
97 }
98
99 hunt(target: string): string {
100 return `Pteranodon nurkuje na ${target} z powietrza`;
101 }
102
103 fly(altitude: number): string {
104 if (altitude > this.maxAltitude) {
105 return 'Wysokość przekracza maksymalny pułap!';
106 }
107 return `Pteranodon leci na wysokości ${altitude}m`;
108 }
109}Stwórz generyczne klasy i funkcje z odpowiednimi constraintami, aby zapewnić bezpieczeństwo typów w całym systemie.
1// Generyczna klasa rejestru z ograniczeniem
2class ParkRegistry<T extends Dinosaur> {
3 private entries: Map<number, T> = new Map();
4
5 add(item: T): void {
6 this.entries.set(item['id'], item);
7 }
8
9 get(id: number): T | undefined {
10 return this.entries.get(id);
11 }
12
13 getAll(): T[] {
14 return Array.from(this.entries.values());
15 }
16
17 // Generyczna metoda z dodatkowym ograniczeniem
18 filter<K extends keyof T>(key: K, value: T[K]): T[] {
19 return this.getAll().filter(item => item[key] === value);
20 }
21
22 // Metoda z wieloma typami generycznymi
23 map<U>(transform: (item: T) => U): U[] {
24 return this.getAll().map(transform);
25 }
26}
27
28// Generyczna funkcja z wieloma ograniczeniami
29function transferDinosaur<
30 T extends Dinosaur,
31 S extends ParkRegistry<T>,
32 D extends ParkRegistry<T>
33>(source: S, destination: D, dinoId: number): boolean {
34 const dino = source.get(dinoId);
35 if (!dino) return false;
36
37 destination.add(dino);
38 return true;
39}
40
41// Generyczny interfejs dla systemów monitoringu
42interface IMonitoringSystem<T extends Dinosaur> {
43 monitor(subject: T): MonitoringReport<T>;
44 getAlerts(): Alert<T>[];
45}
46
47// Generyczny typ raportu
48interface MonitoringReport<T extends Dinosaur> {
49 subject: T;
50 timestamp: Date;
51 status: 'normal' | 'warning' | 'danger';
52 details: string;
53}
54
55interface Alert<T extends Dinosaur> {
56 dinosaur: T;
57 level: 'info' | 'warning' | 'critical';
58 message: string;
59}Wykorzystaj conditional types i mapped types, aby automatycznie dostosowywać zachowanie systemu w zależności od typu dinozaura i poziomu zagrożenia.
1// Typ warunkowy - określa wymagania zagrody na podstawie poziomu zagrożenia
2type DangerLevel = 'low' | 'medium' | 'high' | 'critical';
3
4type EnclosureRequirements<D extends DangerLevel> =
5 D extends 'critical' ? {
6 fenceVoltage: number;
7 guardCount: number;
8 cameraCount: number;
9 backupPower: boolean;
10 emergencyProtocol: string;
11 } :
12 D extends 'high' ? {
13 fenceVoltage: number;
14 guardCount: number;
15 cameraCount: number;
16 backupPower: boolean;
17 } :
18 D extends 'medium' ? {
19 fenceVoltage: number;
20 cameraCount: number;
21 } :
22 {
23 fenceType: string;
24 };
25
26// Użycie typu warunkowego
27const trexEnclosure: EnclosureRequirements<'critical'> = {
28 fenceVoltage: 10000,
29 guardCount: 4,
30 cameraCount: 12,
31 backupPower: true,
32 emergencyProtocol: 'Ewakuacja sektora i wysłanie zespołu interwencyjnego'
33};
34
35// Typy mapowane - generowanie wersji readonly i opcjonalnych
36type DinosaurData = {
37 species: string;
38 health: number;
39 location: string;
40 lastFed: Date;
41 dangerLevel: DangerLevel;
42};
43
44// Mapped type: wersja do wyświetlania (readonly)
45type ReadonlyDinosaurData = {
46 readonly [K in keyof DinosaurData]: DinosaurData[K];
47};
48
49// Mapped type: wersja do aktualizacji (wszystko opcjonalne)
50type DinosaurUpdate = {
51 [K in keyof DinosaurData]?: DinosaurData[K];
52};
53
54// Mapped type: wyciągnij tylko pola typu string
55type StringFieldsOnly<T> = {
56 [K in keyof T as T[K] extends string ? K : never]: T[K];
57};
58
59type DinosaurStringFields = StringFieldsOnly<DinosaurData>;
60// Rezultat: { species: string; location: string; }
61
62// Zaawansowany mapped type z warunkową transformacją
63type DinosaurDisplay<T> = {
64 [K in keyof T]: T[K] extends Date
65 ? string // Daty zamień na stringi
66 : T[K] extends number
67 ? `${number}%` // Liczby z procentem
68 : T[K]; // Reszta bez zmian
69};
70
71// Conditional type z infer
72type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
73
74type DinosaurFactory = (species: string) => Dinosaur;
75type CreatedDino = ExtractReturnType<DinosaurFactory>; // Dinosaur
76
77// Dystrybucyjny conditional type
78type ExcludeDangerLevels<T, U> = T extends U ? never : T;
79type SafeLevels = ExcludeDangerLevels<DangerLevel, 'high' | 'critical'>;
80// Rezultat: 'low' | 'medium'Zastosuj wbudowane utility types oraz stwórz własne, aby precyzyjnie modelować operacje na danych dinozaurów.
1// Wbudowane utility types
2type NewDinosaur = Omit<DinosaurData, 'lastFed'> & {
3 registrationDate: Date;
4};
5
6type DinosaurPreview = Pick<DinosaurData, 'species' | 'dangerLevel'>;
7
8type DinosaurRecord = Record<string, DinosaurData>;
9
10// Wymaganie pól, które wcześniej były opcjonalne
11type RequiredDinosaurUpdate = Required<DinosaurUpdate>;
12
13// Własne utility types
14type DeepReadonly<T> = {
15 readonly [K in keyof T]: T[K] extends object
16 ? T[K] extends Function
17 ? T[K]
18 : DeepReadonly<T[K]>
19 : T[K];
20};
21
22type DeepPartial<T> = {
23 [K in keyof T]?: T[K] extends object
24 ? T[K] extends Function
25 ? T[K]
26 : DeepPartial<T[K]>
27 : T[K];
28};
29
30// Typ wykluczający null i undefined z każdego pola
31type NonNullableFields<T> = {
32 [K in keyof T]: NonNullable<T[K]>;
33};
34
35// System konfiguracji zagrody z pełnym typowaniem
36interface EnclosureConfig {
37 dimensions: {
38 width: number;
39 height: number;
40 depth: number;
41 };
42 security: {
43 fenceType: 'electric' | 'reinforced' | 'laser';
44 voltage?: number;
45 guardSchedule: string[];
46 };
47 environment: {
48 temperature: number;
49 humidity: number;
50 terrain: 'forest' | 'plains' | 'water' | 'mixed';
51 };
52}
53
54// Deep partial pozwala na aktualizację dowolnie zagnieżdżonego pola
55type EnclosureUpdate = DeepPartial<EnclosureConfig>;
56
57const partialUpdate: EnclosureUpdate = {
58 security: {
59 voltage: 15000 // Aktualizuj tylko napięcie
60 }
61};
62
63// Deep readonly dla konfiguracji, która nie powinna być zmieniana
64type FrozenConfig = DeepReadonly<EnclosureConfig>;Zacznij od zdefiniowania interfejsów i typów bazowych - to fundament całego systemu. Następnie stwórz klasę abstrakcyjną
Dinosaur i kilka klas potomnych implementujących różne interfejsy zdolności. Potem zbuduj generyczny ParkRegistry i system monitoringu. Na końcu dodaj typy warunkowe i mapowane, które dynamicznie dostosowują zachowanie systemu.Pamiętaj o następującej strukturze plików:
1// 1. Typy i interfejsy bazowe
2// 2. Klasa abstrakcyjna Dinosaur
3// 3. Klasy potomne (TRex, Velociraptor, Triceratops, Pteranodon...)
4// 4. Generyczny ParkRegistry<T>
5// 5. System monitoringu z typami warunkowymi
6// 6. Utility types i transformacje
7// 7. Główna klasa JurassicParkManager łącząca wszystkoPrzykład inicjalizacji systemu:
1// Tworzenie rejestru
2const carnivoreRegistry = new ParkRegistry<TyrannosaurusRex>();
3const herbivoreRegistry = new ParkRegistry<Triceratops>();
4
5// Dodawanie dinozaurów
6const rex = new TyrannosaurusRex();
7carnivoreRegistry.add(rex);
8
9// Konfiguracja zagrody z typami warunkowymi
10const criticalEnclosure: EnclosureRequirements<'critical'> = {
11 fenceVoltage: 10000,
12 guardCount: 4,
13 cameraCount: 12,
14 backupPower: true,
15 emergencyProtocol: 'Kod Czerwony - pełna ewakuacja'
16};
17
18// Generowanie raportu
19const reports = carnivoreRegistry.map(dino => ({
20 species: dino.species,
21 status: dino.getStatus(),
22 danger: dino.dangerLevel
23}));
24
25console.log('=== Raport Parku Jurajskiego ===');
26reports.forEach(r => console.log(`${r.species}: ${r.status} [Zagrożenie: ${r.danger}]`));"Pamiętaj," mówi Dr. Wu, poprawiając okulary, "system typów TypeScript to Twoje DNA - fundament, na którym budujesz cały ekosystem. Im lepiej zaprojektujesz typy, tym stabilniejszy będzie Twój Park. Generyki dają elastyczność, typy warunkowe - inteligencję, a typy mapowane - zdolność do transformacji. Połącz je wszystkie i stwórz system, który przetrwa próbę czasu... i próbę uciekających dinozaurów."
Czas zastosować wszystko, czego się nauczyłeś. Otwórz edytor i zbuduj swój zaawansowany system typów!