"In Jurassic Park, we have protocols and standards that apply to all objects of a given class," explains Dr. Henry Wu, the park's chief geneticist. "For example, all our dinosaur enclosures follow the same security procedure, regardless of which specific enclosure we're considering. On the other hand, every dinosaur species has its own unique DNA pattern, but we all know that DNA must exist - even if the details differ between species."
In object-oriented programming, TypeScript offers two powerful concepts that reflect these ideas:
In this exercise, we will dive deep into these concepts by creating an advanced genetics and security protocol management system for Jurassic Park.
Static elements in a class belong to the class itself, not to its instances. This means:
Let's see how static elements can be used in the context of Jurassic Park:
1class SecurityProtocol {
2 // Static property - shared by all instances
3 static readonly LEVEL_CRITICAL: number = 5;
4 static readonly LEVEL_HIGH: number = 4;
5 static readonly LEVEL_MEDIUM: number = 3;
6 static readonly LEVEL_LOW: number = 2;
7 static readonly LEVEL_MINIMAL: number = 1;
8
9 // Static property storing the current global park threat level
10 private static _currentThreatLevel: number = SecurityProtocol.LEVEL_LOW;
11
12 // Instance properties
13 private readonly area: string;
14 private localThreatLevel: number;
15
16 constructor(area: string) {
17 this.area = area;
18 this.localThreatLevel = SecurityProtocol._currentThreatLevel;
19 console.log(`Created security protocol for area: ${area}`);
20 }
21
22 // Static method accessible directly on the class
23 static raiseGlobalThreatLevel(): void {
24 if (this._currentThreatLevel < this.LEVEL_CRITICAL) {
25 this._currentThreatLevel++;
26 console.log(`WARNING: Raised global threat level to ${this._currentThreatLevel}`);
27
28 // Calling another static method
29 this.notifyAboutThreatLevel();
30 } else {
31 console.log("WARNING: Maximum threat level already reached!");
32 }
33 }
34
35 // Another static method
36 static lowerGlobalThreatLevel(): void {
37 if (this._currentThreatLevel > this.LEVEL_MINIMAL) {
38 this._currentThreatLevel--;
39 console.log(`Lowered global threat level to ${this._currentThreatLevel}`);
40 } else {
41 console.log("Threat level is already at minimum.");
42 }
43 }
44
45 // Static getter
46 static get currentThreatLevel(): number {
47 return this._currentThreatLevel;
48 }
49
50 // Private static method
51 private static notifyAboutThreatLevel(): void {
52 console.log(`Notification: Current park threat level: ${this._currentThreatLevel}`);
53 }
54
55 // Instance method using static properties
56 updateLocalThreatLevel(): void {
57 // Updating the local level based on the global level
58 this.localThreatLevel = SecurityProtocol._currentThreatLevel;
59 console.log(`Obszar ${this.area}: updated threat level to ${this.localThreatLevel}`);
60 }
61
62 get securityStatus(): string {
63 const poziomy = [
64 "UNKNOWN",
65 "MINIMAL",
66 "LOW",
67 "MEDIUM",
68 "HIGH",
69 "CRITICAL"
70 ];
71
72 return `Area: ${this.area}, Threat level: ${poziomy[this.localThreatLevel]}`;
73 }
74}
75
76// Using static properties and methods - without creating instances!
77console.log("Default threat level:", SecurityProtocol.currentThreatLevel);
78
79// Calling a static method directly on the class
80SecurityProtocol.raiseGlobalThreatLevel();
81SecurityProtocol.raiseGlobalThreatLevel();
82
83// Creating instances of the class
84const centralProtocol = new SecurityProtocol("Operations Center");
85const enclosureProtocol = new SecurityProtocol("T-Rex Enclosure");
86
87// Displaying the security status
88console.log(centralProtocol.securityStatus);
89console.log(enclosureProtocol.securityStatus);
90
91// Raising the global threat level again
92SecurityProtocol.raiseGlobalThreatLevel();
93
94// Updating local threat levels
95centralProtocol.updateLocalThreatLevel();
96enclosureProtocol.updateLocalThreatLevel();
97
98// Checking updated statuses
99console.log(centralProtocol.securityStatus);
100console.log(enclosureProtocol.securityStatus);In the above example:
_currentThreatLevel is shared across all instancesStatic elements are especially useful in the following cases:
Here's a more elaborate example showing different uses of static elements:
1class GeneticRegistry {
2 // Static constants
3 static readonly NUCLEOTIDE_BASE = ["A", "C", "G", "T"];
4 static readonly MIN_DNA_LENGTH = 1000000;
5
6 // Global state
7 private static _registeredSpeciesCount: number = 0;
8 private static _speciesRegistrations: Map<string, string> = new Map();
9
10 // Instance properties
11 private id: string;
12 private dnaSequence: string;
13 private nazwa: string;
14
15 constructor(nazwa: string, dnaSequence: string) {
16 if (dnaSequence.length < GeneticRegistry.MIN_DNA_LENGTH) {
17 throw new Error(`DNA sequence is too short. Minimum required: ${GeneticRegistry.MIN_DNA_LENGTH} base pairs.`);
18 }
19
20 this.nazwa = nazwa;
21 this.dnaSequence = dnaSequence;
22 this.id = GeneticRegistry.wygenerujId(nazwa);
23
24 // Registering a new species
25 GeneticRegistry._speciesRegistrations.set(this.id, nazwa);
26 GeneticRegistry._registeredSpeciesCount++;
27 }
28
29 // Utility method - creates an ID based on name
30 private static wygenerujId(nazwa: string): string {
31 const timestamp = Date.now();
32 const cleanName = nazwa.toLowerCase().replace(/[^a-z0-9]/g, "");
33 return `DNO-${cleanName}-${timestamp}`;
34 }
35
36 // Factory method - creates an instance with random DNA
37 static createTestSpecies(nazwa: string): GeneticRegistry {
38 const dnaLength = this.MIN_DNA_LENGTH + Math.floor(Math.random() * 500000);
39 let sequence = "";
40
41 for (let i = 0; i < dnaLength; i++) {
42 const randomIndex = Math.floor(Math.random() * this.NUCLEOTIDE_BASE.length);
43 sequence += this.NUCLEOTIDE_BASE[randomIndex];
44 }
45
46 return new GeneticRegistry(nazwa, sequence);
47 }
48
49 // Utility method - verifies the correctness of a DNA sequence
50 static verifySequence(sequence: string): boolean {
51 if (sequence.length < this.MIN_DNA_LENGTH) return false;
52
53 // Checks if the sequence contains only allowed nucleotides
54 for (const nucleotide of sequence) {
55 if (!this.NUCLEOTIDE_BASE.includes(nucleotide)) {
56 return false;
57 }
58 }
59
60 return true;
61 }
62
63 // Getter for the static property
64 static get registeredSpeciesCount(): number {
65 return this._registeredSpeciesCount;
66 }
67
68 // Static method returning all registered species
69 static getAllSpecies(): string[] {
70 return Array.from(this._speciesRegistrations.values());
71 }
72
73 // Instance method using static fields
74 analyzeSequence(): { proportions: Record<string, number>, length: number } {
75 const proportions: Record<string, number> = {};
76
77 // Initializing counters for each nucleotide
78 GeneticRegistry.NUCLEOTIDE_BASE.forEach(n => {
79 proportions[n] = 0;
80 });
81
82 // Counting nucleotides
83 for (const nucleotide of this.dnaSequence) {
84 proportions[nucleotide]++;
85 }
86
87 // Converting to percentages
88 GeneticRegistry.NUCLEOTIDE_BASE.forEach(n => {
89 proportions[n] = Number((proportions[n] / this.dnaSequence.length * 100).toFixed(2));
90 });
91
92 return {
93 proportions,
94 length: this.dnaSequence.length
95 };
96 }
97
98 // Instance getters
99 get identyfikator(): string {
100 return this.id;
101 }
102
103 get gatunekName(): string {
104 return this.nazwa;
105 }
106}
107
108// Using static methods and properties
109console.log("Minimum DNA length:", GeneticRegistry.MIN_DNA_LENGTH);
110console.log("Available nucleotides:", GeneticRegistry.NUCLEOTIDE_BASE.join(", "));
111
112// Verifying a sample sequence
113const exampleSequence = "ACGTACGTACGT".repeat(100000); // Repeating to reach min. length
114console.log("Sequence verification:", GeneticRegistry.verifySequence(exampleSequence));
115
116// Creating instances using the factory method
117const trex = GeneticRegistry.createTestSpecies("Tyrannosaurus Rex");
118const raptor = GeneticRegistry.createTestSpecies("Velociraptor");
119const trike = GeneticRegistry.createTestSpecies("Triceratops");
120
121// Checking how many species have been registered
122console.log("Number of registered species:", GeneticRegistry.registeredSpeciesCount);
123console.log("Species list:", GeneticRegistry.getAllSpecies());
124
125// Using instance methods
126const trexAnalysis = trex.analyzeSequence();
127console.log(`Analiza ${trex.gatunekName} (ID: ${trex.identyfikator}):`);
128console.log(`Sequence length: ${trexAnalysis.length} base pairs`);
129console.log("Nucleotide proportions:", trexAnalysis.proportions);This example demonstrates:
NUCLEOTIDE_BASE, MIN_DNA_LENGTH)verifySequence and wygenerujIdcreateTestSpecies creating instances with random dataAbstract classes in TypeScript define a template that must be implemented by inheriting classes. You cannot create an instance of an abstract class directly - it serves as a base "blueprint" for other classes.
Abstract methods, defined in an abstract class, must be implemented by inheriting classes. They define a contract specifying which methods must be available, but they don't contain their own implementation.
Let's see how to use abstract classes in the context of Jurassic Park:
1// Abstract base class for all dinosaurs
2abstract class Dinosaur {
3 // Properties common to all dinosaurs
4 private _id: string;
5 protected _nazwa: string;
6 protected _species: string;
7 protected _age: number;
8 protected _weight: number;
9
10 constructor(id: string, nazwa: string, gatunek: string, wiek: number, weight: number) {
11 this._id = id;
12 this._name = name;
13 this._species = species;
14 this._age = age;
15 this._weight = weight;
16 }
17
18 // Getters
19 get id(): string { return this._id; }
20 get nazwa(): string { return this._name; }
21 get species(): string { return this._species; }
22 get age(): number { return this._age; }
23 get weight(): number { return this._weight; }
24
25 // Methods implemented in all derived classes
26 abstract calculateCaloricNeeds(): number;
27 abstract determineThreatLevel(): number;
28
29 // Abstract property - must be implemented in derived classes
30 abstract get typDiety(): string;
31
32 // Concrete method (not abstract) - available in all derived classes
33 getBasicData(): string {
34 return `${this._name} (${this._gatunek}) - ID: ${this._id}, Age: ${this._wiek} lat, Weight: ${this._weight} kg`;
35 }
36
37 // Method using abstract methods
38 generateReportDinosaura(): string {
39 const kalorie = this.calculateCaloricNeeds();
40 const threatLevel = this.determineThreatLevel();
41
42 return `
43=== RAPORT DINOZAURA ===
44${this.getBasicData()}
45Typ diety: ${this.typDiety}
46Dzienne zapotrzebowanie kaloryczne: ${kalorie} kcal
47Threat level: ${threatLevel}/10
48======================
49 `;
50 }
51}
52
53// Class inheriting from the abstract class
54class PredatoryDinosaur extends Dinosaur {
55 private _biteForce: number;
56 private _runSpeed: number;
57
58 constructor(
59 id: string,
60 nazwa: string,
61 gatunek: string,
62 wiek: number,
63 weight: number,
64 biteForce: number,
65 runSpeed: number
66 ) {
67 super(id, nazwa, gatunek, wiek, weight);
68 this._biteForce = biteForce;
69 this._runSpeed = runSpeed;
70 }
71
72 // Getters
73 get biteForce(): number { return this._biteForce; }
74 get runSpeed(): number { return this._runSpeed; }
75
76 // Implementation of abstract property
77 get typDiety(): string {
78 return "carnivore";
79 }
80
81 // Implementations of abstract methods
82 calculateCaloricNeeds(): number {
83 // For predators we use a complex formula
84 return Math.round(this._weight * 0.6 * (this._runSpeed * 0.2));
85 }
86
87 determineThreatLevel(): number {
88 // Threat level depends on bite force, speed, and weight
89 const bazowy = (this._biteForce * 0.7 + this._runSpeed * 0.3) / 100;
90 return Math.min(10, Math.max(1, Math.round(bazowy)));
91 }
92
93 // Method specific to predators
94 poluj(): string {
95 return `${this._name} hunts at speed ${this._runSpeed} km/h and bite force ${this._biteForce} PSI`;
96 }
97}
98
99// Another inheriting class
100class HerbivorousDinosaur extends Dinosaur {
101 private _neckLength: number;
102 private _armorType: "none" | "light" | "medium" | "heavy";
103
104 constructor(
105 id: string,
106 nazwa: string,
107 gatunek: string,
108 wiek: number,
109 weight: number,
110 neckLength: number,
111 armorType: "none" | "light" | "medium" | "heavy"
112 ) {
113 super(id, nazwa, gatunek, wiek, weight);
114 this._neckLength = neckLength;
115 this._armorType = armorType;
116 }
117
118 // Getters
119 get neckLength(): number { return this._neckLength; }
120 get armorType(): string { return this._armorType; }
121
122 // Implementation of abstract property
123 get typDiety(): string {
124 return "herbivorous";
125 }
126
127 // Implementations of abstract methods
128 calculateCaloricNeeds(): number {
129 // For herbivores we use a simpler formula - they need more calories per kilogram
130 return Math.round(this._weight * 0.8);
131 }
132
133 determineThreatLevel(): number {
134 // Threat level is lower for herbivores, but depends on weight and armor type
135 let bazowy = this._weight / 1000;
136
137 // Modifier dependent on armor
138 const modyfikatoryPancerza = {
139 "none": 0,
140 "light": 1,
141 "medium": 2,
142 "heavy": 3
143 };
144
145 bazowy += modyfikatoryPancerza[this._armorType];
146
147 return Math.min(7, Math.max(1, Math.round(bazowy))); // Max. 7 for herbivores
148 }
149
150 // Method specific to herbivores
151 graze(): string {
152 const reachHeight = this._neckLength > 2 ? "tall" : "low";
153 return `${this._name} is grazing, eating plants from ${reachHeight} tree sections`;
154 }
155}
156
157// We cannot create an instance of an abstract class
158// const invalid = new Dinosaur("ID", "Name", "Species", 1, 100); // Error!
159
160// Creating instances of derived classes
161const trex = new PredatoryDinosaur("T-REX-1", "Rex", "Tyrannosaurus", 8, 7000, 12800, 32);
162const stegosaurus = new HerbivorousDinosaur("STEG-1", "Spike", "Stegosaurus", 12, 5000, 1.2, "heavy");
163const diplodocus = new HerbivorousDinosaur("DIPL-1", "Dippy", "Diplodocus", 15, 16000, 6.5, "none");
164
165// Using methods implemented in derived classes
166console.log(trex.generateReportDinosaura());
167console.log(stegosaurus.generateReportDinosaura());
168console.log(diplodocus.generateReportDinosaura());
169
170// Using methods specific to a given class
171console.log(trex.poluj());
172console.log(stegosaurus.graze());
173
174// For both types of dinosaurs we can use base class methods
175console.log(trex.getBasicData());
176console.log(diplodocus.getBasicData());In the above example:
Dinosaur is an abstract class containing shared properties and methodscalculateCaloricNeeds() and determineThreatLevel() must be implemented by inheriting classestypDiety must be defined in derived classesPredatoryDinosaur and HerbivorousDinosaur implement the abstract methods and propertiesDinosaur class directlyAbstract classes are especially useful in the following cases:
Let's see a more advanced example combining abstract classes and methods with static elements in a security protocol management system for Jurassic Park:
1// Helper types
2type AlertLevel = "normall" | "elevated" | "high" | "critical";
3type StatusSystem = "aktywny" | "nieaktywny" | "w_konserwacji" | "awaria";
4
5// Interface for monitoring systems
6interface MonitoringSystem {
7 startMonitoring(): void;
8 stopMonitoring(): void;
9 pobierzAktualnyStatus(): StatusSystem;
10}
11
12// Abstract base class for all security systems
13abstract class SecuritySystem implements MonitoringSystem {
14 // Static properties - for all security systems
15 private static _parkAlertLevel: AlertLevel = "normall";
16 private static _aktywneSystemy: Set<string> = new Set();
17 private static _incydenty: Array<{data: Date, systemType: string, location: string, description: string}> = [];
18
19 // Regular properties
20 protected _id: string;
21 protected _location: string;
22 protected _status: StatusSystem;
23 protected _czyMonitorowany: boolean;
24 protected _reviewDate: Date;
25
26 constructor(id: string, location: string) {
27 this._id = id;
28 this._location = location;
29 this._status = "nieaktywny";
30 this._isMonitored = false;
31 this._reviewDate = new Date();
32
33 // System registration
34 SecuritySystem.registerSystem(id, this.constructor.name);
35 }
36
37 // Static methods
38 static registerSystem(id: string, systemType: string): void {
39 console.log(`Registered new security system: ${systemType} (ID: ${id})`);
40 }
41
42 static reportIncident(systemType: string, location: string, description: string): void {
43 const incydent = {
44 data: new Date(),
45 systemType,
46 location,
47 description
48 };
49
50 this._incydenty.push(incydent);
51 console.log(`ALERT: Incident reported at location ${location}: ${description}`);
52
53 // Automatically raising the alert level
54 if (this._parkAlertLevel === "normall") {
55 this.changeAlertLevel("elevated");
56 } else if (this._parkAlertLevel === "elevated") {
57 this.changeAlertLevel("high");
58 } else if (this._parkAlertLevel === "high") {
59 this.changeAlertLevel("critical");
60 }
61 }
62
63 static changeAlertLevel(newLevel: AlertLevel): void {
64 this._parkAlertLevel = newLevel;
65 console.log(`WARNING: Changed park alert level to: ${newLevel.toUpperCase()}`);
66 }
67
68 static get alertLevel(): AlertLevel {
69 return this._parkAlertLevel;
70 }
71
72 static get recentIncidents(): Array<{data: Date, systemType: string, location: string, description: string}> {
73 // Returning the 5 most recent incidents
74 return [...this._incydenty].slice(-5);
75 }
76
77 // Instance getters
78 get id(): string { return this._id; }
79 get location(): string { return this._location; }
80 get status(): StatusSystem { return this._status; }
81
82 // Implementation of MonitoringSystem interface
83 startMonitoring(): void {
84 this._isMonitored = true;
85 SecuritySystem._aktywneSystemy.add(this._id);
86 console.log(`Monitoring started for systemu ${this.constructor.name} w lokalizacji ${this._location}`);
87 }
88
89 stopMonitoring(): void {
90 this._isMonitored = false;
91 SecuritySystem._aktywneSystemy.delete(this._id);
92 console.log(`Monitoring stopped for systemu ${this.constructor.name} w lokalizacji ${this._location}`);
93 }
94
95 pobierzAktualnyStatus(): StatusSystem {
96 return this._status;
97 }
98
99 // Abstract methods that must be implemented
100 abstract uruchom(): void;
101 abstract zatrzymaj(): void;
102 abstract performReview(): void;
103
104 // Abstract properties
105 abstract get systemType(): string;
106 abstract get poziomPriorytetuAlarmowego(): number; // 1-10
107
108 // Method using abstract properties and methods
109 generateStatusReport(): string {
110 return `
111=== SECURITY SYSTEM REPORT ===
112ID: ${this._id}
113Typ: ${this.systemType}
114Lokalizacja: ${this._location}
115Status: ${this._status}
116Monitorowany: ${this._isMonitored ? "TAK" : "NIE"}
117Priorytet alarmowy: ${this.poziomPriorytetuAlarmowego}/10
118Last review: ${this._reviewDate.toLocaleDateString()}
119Global alert level: ${SecuritySystem._parkAlertLevel.toUpperCase()}
120=================================
121 `;
122 }
123}
124
125// Derived class - Electric Fence System
126class ElectricFenceSystem extends SecuritySystem {
127 private _voltage: number; // w woltach
128 private _fenceLength: number; // w metrach
129 private _securityType: "standard" | "reinforced" | "ultra-reinforced";
130
131 constructor(
132 id: string,
133 location: string,
134 fenceLength: number,
135 typZabezpieczenia: "standard" | "reinforced" | "ultra-reinforced"
136 ) {
137 super(id, location);
138 this._voltage = 0;
139 this._fenceLength = fenceLength;
140 this._securityType = typZabezpieczenia;
141 }
142
143 // Implementation of abstract properties
144 get systemType(): string {
145 return "Ogrodzenie Elektryczne";
146 }
147
148 get poziomPriorytetuAlarmowego(): number {
149 // Priority depends on the security type
150 const bazowy = this._securityType === "standard" ? 7 :
151 this._securityType === "reinforced" ? 8 : 10;
152
153 // We take fence length into account - longer ones have higher priority
154 const modyfikator = Math.min(2, Math.floor(this._fenceLength / 1000));
155
156 return Math.min(10, bazowy + modyfikator);
157 }
158
159 // Implementation of abstract methods
160 uruchom(): void {
161 if (this._status === "w_konserwacji") {
162 console.log(`ERROR: Cannot start system ${this._id} - is under maintenance`);
163 return;
164 }
165
166 this._status = "aktywny";
167
168 // Voltage depends on the security type
169 this._voltage = this._securityType === "standard" ? 10000 :
170 this._securityType === "reinforced" ? 20000 : 30000;
171
172 console.log(`Uruchomiono system ogrodzenia ${this._id} w lokalizacji ${this._location}`);
173 console.log(`Voltage: ${this._voltage}V, Length: ${this._fenceLength}m`);
174 }
175
176 zatrzymaj(): void {
177 this._status = "nieaktywny";
178 this._voltage = 0;
179 console.log(`Zatrzymano system ogrodzenia ${this._id} w lokalizacji ${this._location}`);
180
181 // Reporting an incident at high alert level
182 if (SecuritySystem.alertLevel === "high" || SecuritySystem.alertLevel === "critical") {
183 SecuritySystem.reportIncident(
184 this.systemType,
185 this._location,
186 `Fence shutdown at high alert level!`
187 );
188 }
189 }
190
191 performReview(): void {
192 const poprzedniStatus = this._status;
193 this._status = "w_konserwacji";
194 console.log(`Started fence system review ${this._id}`);
195
196 // Simulation of inspection
197 setTimeout(() => {
198 this._reviewDate = new Date();
199 this._status = poprzedniStatus;
200 console.log(`Completed fence system review ${this._id}`);
201 }, 2000);
202 }
203
204 // Methods specific to this system type
205 changeVoltage(newVoltage: number): void {
206 if (this._status !== "aktywny") {
207 console.log(`ERROR: Cannot change voltage - system is not active`);
208 return;
209 }
210
211 this._voltage = newVoltage;
212 console.log(`Changed fence voltage ${this._id} na ${newVoltage}V`);
213 }
214
215 reportFenceBreach(): void {
216 this._status = "awaria";
217 SecuritySystem.reportIncident(
218 this.systemType,
219 this._location,
220 `Electric fence breach detected!`
221 );
222 }
223}
224
225// Another derived class - Video Monitoring System
226class VideoMonitoringSystem extends SecuritySystem {
227 private _cameraCount: number;
228 private _resolution: "SD" | "HD" | "4K";
229 private _trybNocny: boolean;
230
231 constructor(
232 id: string,
233 location: string,
234 countKamer: number,
235 resolution: "SD" | "HD" | "4K"
236 ) {
237 super(id, location);
238 this._cameraCount = countKamer;
239 this._resolution = resolution;
240 this._trybNocny = false;
241 }
242
243 // Implementation of abstract properties
244 get systemType(): string {
245 return "Monitoring Wideo";
246 }
247
248 get poziomPriorytetuAlarmowego(): number {
249 // Priority depends on the number of cameras and resolution
250 const bazowy = 5;
251
252 // Resolution modifier
253 const resolutionModifier =
254 this._resolution === "SD" ? 0 :
255 this._resolution === "HD" ? 1 : 2;
256
257 // Camera count modifier
258 const cameraModifier = Math.min(2, Math.floor(this._cameraCount / 10));
259
260 return Math.min(10, bazowy + resolutionModifier + cameraModifier);
261 }
262
263 // Implementation of abstract methods
264 uruchom(): void {
265 this._status = "aktywny";
266 console.log(`Uruchomiono system monitoringu ${this._id} w lokalizacji ${this._location}`);
267 console.log(`Camera count: ${this._cameraCount}, Resolution: ${this._resolution}`);
268 }
269
270 zatrzymaj(): void {
271 this._status = "nieaktywny";
272 console.log(`Zatrzymano system monitoringu ${this._id} w lokalizacji ${this._location}`);
273 }
274
275 performReview(): void {
276 this._status = "w_konserwacji";
277 console.log(`Started monitoring system review ${this._id}`);
278
279 // In this case, the inspection completes immediately
280 this._reviewDate = new Date();
281 this._status = "aktywny";
282 console.log(`Completed monitoring system review ${this._id}`);
283 }
284
285 // Methods specific to this system type
286 enableNightMode(): void {
287 this._trybNocny = true;
288 console.log(`Enabled night mode in monitoring system ${this._id}`);
289 }
290
291 disableNightMode(): void {
292 this._trybNocny = false;
293 console.log(`Disabled night mode in monitoring system ${this._id}`);
294 }
295
296 detectMotion(area: string): void {
297 console.log(`System ${this._id}: Wykryto ruch w areaze ${area}`);
298
299 if (SecuritySystem.alertLevel !== "normall") {
300 SecuritySystem.reportIncident(
301 this.systemType,
302 `${this._location} - area ${area}`,
303 `Wykryto nieautoryzowany ruch!`
304 );
305 }
306 }
307}
308
309// Demonstration of Jurassic Park security system
310function testSecuritySystem() {
311 console.log("=== INITIALIZING JURASSIC PARK SECURITY SYSTEMS ===");
312
313 // Creating fence systems
314 const mainFence = new ElectricFenceSystem("FNC-001", "Sector A - Main entrance", 2500, "ultra-reinforced");
315 const tRexFence = new ElectricFenceSystem("FNC-002", "Sektor B - Wybieg T-Rex", 1200, "ultra-reinforced");
316 const raptorFence = new ElectricFenceSystem("FNC-003", "Sector C - Raptor Enclosure", 800, "reinforced");
317
318 // Creating monitoring systems
319 const mainMonitoring = new VideoMonitoringSystem("CAM-001", "Control center", 20, "4K");
320 const monitoringTRex = new VideoMonitoringSystem("CAM-002", "Sektor B - Wybieg T-Rex", 12, "HD");
321 const raptorMonitoring = new VideoMonitoringSystem("CAM-003", "Sector C - Raptor Enclosure", 8, "HD");
322
323 // Starting all systems
324 console.log("\n=== STARTING SYSTEMS ===");
325 mainFence.uruchom();
326 tRexFence.uruchom();
327 raptorFence.uruchom();
328
329 mainMonitoring.uruchom();
330 monitoringTRex.uruchom();
331 raptorMonitoring.uruchom();
332
333 // Starting monitoring
334 console.log("\n=== AKTYWACJA MONITOROWANIA ===");
335 mainFence.startMonitoring();
336 tRexFence.startMonitoring();
337 raptorFence.startMonitoring();
338
339 mainMonitoring.startMonitoring();
340 monitoringTRex.startMonitoring();
341 raptorMonitoring.startMonitoring();
342
343 // Checking statuses
344 console.log("\n=== SYSTEM REPORTS ===");
345 console.log(mainFence.generateStatusReport());
346 console.log(monitoringTRex.generateStatusReport());
347
348 // Simulating an incident
349 console.log("\n=== SYMULACJA INCYDENTU ===");
350 console.log("Current alert level:", SecuritySystem.alertLevel);
351
352 raptorMonitoring.detectMotion("South corner");
353 raptorFence.reportFenceBreach();
354
355 // Checking alert level after incidents
356 console.log("\nCurrent alert level after incidents:", SecuritySystem.alertLevel);
357
358 // Displaying recent incidents
359 console.log("\n=== OSTATNIE INCYDENTY ===");
360 SecuritySystem.recentIncidents.forEach((incydent, index) => {
361 console.log(`${index + 1}. [${incydent.data.toLocaleString()}] ${incydent.systemType} w ${incydent.location}: ${incydent.description}`);
362 });
363
364 // Conducting inspection
365 console.log("\n=== SYSTEM REVIEW ===");
366 tRexFence.performReview();
367 mainMonitoring.performReview();
368
369 // Manual alert level change
370 console.log("\n=== NORMALIZACJA SYTUACJI ===");
371 SecuritySystem.changeAlertLevel("normall");
372}
373
374// Running the test
375testSecuritySystem();In this advanced example:
SecuritySystem as the base for all security systemsElectricFenceSystem and VideoMonitoringSystem) implement the abstract elementsStatic and abstract elements can be a powerful tool when used together in a well-designed class hierarchy. Here are some patterns and examples of their application:
1abstract class DinosaurSpecies {
2 // Static properties
3 private static _zarejestrowaneDinosaurs: Map<string, DinosaurSpecies> = new Map();
4
5 // Instance properties
6 private _name: string;
7 private _eraDominocenitryczna: string;
8 protected _dietType: "carnivore" | "herbivorous" | "omnivore";
9
10 constructor(nazwa: string, era: string, dieta: "carnivore" | "herbivorous" | "omnivore") {
11 this._name = name;
12 this._eraDominocenitryczna = era;
13 this._dietType = dieta;
14
15 // Adding to registry
16 DinosaurSpecies._zarejestrowaneDinosaury.set(nazwa, this);
17 }
18
19 // Static factory method - creates the appropriate species based on diet
20 static createSpecies(
21 nazwa: string,
22 era: string,
23 dieta: "carnivore" | "herbivorous" | "omnivore",
24 additionalParams: Record<string, any>
25 ): DinosaurSpecies {
26 if (dieta === "carnivore") {
27 return new CarnivoreSpecies(
28 nazwa,
29 era,
30 additionalParams.biteForce,
31 additionalParams.runSpeed
32 );
33 } else {
34 return new HerbivoreSpecies(
35 nazwa,
36 era,
37 additionalParams.neckLength || 0,
38 additionalParams.hasArmor || false
39 );
40 }
41 }
42
43 // Static method for retrieving registered species
44 static getSpecies(nazwa: string): DinosaurSpecies | undefined {
45 return this._zarejestrowaneDinosaury.get(nazwa);
46 }
47
48 static get allSpecies(): string[] {
49 return Array.from(this._zarejestrowaneDinosaury.keys());
50 }
51
52 // Instance getters
53 get nazwa(): string { return this._name; }
54 get era(): string { return this._eraDominocenitryczna; }
55 get typDiety(): string { return this._dietType; }
56
57 // Abstract methods
58 abstract descriptionZachowania(): string;
59 abstract estimateWeight(length: number): number;
60
61 // Method using abstract methods
62 generujOpis(): string {
63 return `${this._name} - dinosaur ${this._dietType} z ery ${this._eraDominocenitryczna}.
64${this.descriptionZachowania()}`;
65 }
66}
67
68// Derived classes
69class CarnivoreSpecies extends DinosaurSpecies {
70 private _biteForce: number; // w PSI
71 private _runSpeed: number; // w km/h
72
73 constructor(nazwa: string, era: string, biteForce: number, runSpeed: number) {
74 super(nazwa, era, "carnivore");
75 this._biteForce = biteForce;
76 this._runSpeed = runSpeed;
77 }
78
79 // Getters
80 get biteForce(): number { return this._biteForce; }
81 get runSpeed(): number { return this._runSpeed; }
82
83 // Implementation of abstract methods
84 descriptionZachowania(): string {
85 return `Hunts at speed ${this._runSpeed} km/h and bite force ${this._biteForce} PSI.`;
86 }
87
88 estimateWeight(length: number): number {
89 // Approximate formula for predators
90 return length * length * 100;
91 }
92}
93
94class HerbivoreSpecies extends DinosaurSpecies {
95 private _neckLength: number; // w metrach
96 private _hasArmor: boolean;
97
98 constructor(nazwa: string, era: string, neckLength: number, hasArmor: boolean) {
99 super(nazwa, era, "herbivorous");
100 this._neckLength = neckLength;
101 this._hasArmor = hasArmor;
102 }
103
104 // Getters
105 get neckLength(): number { return this._neckLength; }
106 get hasArmor(): boolean { return this._hasArmor; }
107
108 // Implementation of abstract methods
109 descriptionZachowania(): string {
110 const descriptionSzyi = this._neckLength > 3
111 ? `Has a long neck (${this._neckLength}m) allowing it to eat tall vegetation.`
112 : "Feeds on low vegetation.";
113
114 const descriptionPancerza = this._hasArmor
115 ? "Has armor for protection against predators."
116 : "Nie posiada pancerza ochronnego.";
117
118 return `${descriptionSzyi} ${descriptionPancerza}`;
119 }
120
121 estimateWeight(length: number): number {
122 // Approximate formula for herbivores (usually heavier)
123 let weight = length * length * 150;
124
125 // Additional weight for armor
126 if (this._hasArmor) {
127 weight *= 1.3;
128 }
129
130 return weight;
131 }
132}
133
134// Usage
135function testDinosaurSpecies() {
136 // Creating species using the factory method
137 DinosaurSpecies.createSpecies(
138 "Tyrannosaurus Rex",
139 "late Cretaceous",
140 "carnivore",
141 { biteForce: 12800, runSpeed: 32 }
142 );
143
144 DinosaurSpecies.createSpecies(
145 "Velociraptor",
146 "late Cretaceous",
147 "carnivore",
148 { biteForce: 2000, runSpeed: 64 }
149 );
150
151 DinosaurSpecies.createSpecies(
152 "Brachiosaurus",
153 "late Jurassic",
154 "herbivorous",
155 { neckLength: 9, hasArmor: false }
156 );
157
158 DinosaurSpecies.createSpecies(
159 "Stegosaurus",
160 "late Jurassic",
161 "herbivorous",
162 { neckLength: 0.5, hasArmor: true }
163 );
164
165 // Accessing registered species
166 console.log("Available species:", DinosaurSpecies.allSpecies);
167
168 // Retrieving species information
169 const trex = DinosaurSpecies.getSpecies("Tyrannosaurus Rex");
170 const brach = DinosaurSpecies.getSpecies("Brachiosaurus");
171
172 if (trex) {
173 console.log(trex.generujOpis());
174 console.log(`Estimated weight (12m long): ${trex.estimateWeight(12)} kg`);
175 }
176
177 if (brach) {
178 console.log(brach.generujOpis());
179 console.log(`Estimated weight (25m long): ${brach.estimateWeight(25)} kg`);
180 }
181
182 // Type checking
183 const steg = DinosaurSpecies.getSpecies("Stegosaurus") as HerbivoreSpecies;
184 if (steg && steg instanceof HerbivoreSpecies) {
185 console.log(`Stegosaurus posiada pancerz: ${steg.hasArmor ? "TAK" : "NIE"}`);
186 }
187
188 const raptor = DinosaurSpecies.getSpecies("Velociraptor") as CarnivoreSpecies;
189 if (raptor && raptor instanceof CarnivoreSpecies) {
190 console.log(`Velociraptor - run speed: ${raptor.runSpeed} km/h`);
191 }
192}
193
194testDinosaurSpecies();In TypeScript since version 4.0, it's possible to define abstract static methods. This enables creating class hierarchies where derived classes must implement specific static methods:
1abstract class LocationSystem {
2 // Instance properties
3 protected _id: string;
4 protected _nazwa: string;
5
6 constructor(id: string, nazwa: string) {
7 this._id = id;
8 this._name = name;
9 }
10
11 // Abstract static method that derived classes must implement
12 static abstract generateLocationId(typ: string, numer: number): string;
13
14 // Regular abstract instance method
15 abstract pobierzInformacjeOLokacji(): Record<string, any>;
16
17 // Instance method
18 generateReport(): string {
19 const info = this.pobierzInformacjeOLokacji();
20
21 let raport = `=== RAPORT LOKACJI: ${this._name} (ID: ${this._id}) ===\n`;
22
23 for (const [klucz, value] of Object.entries(info)) {
24 raport += `${klucz}: ${value}\n`;
25 }
26
27 return raport;
28 }
29}
30
31class DinosaurEnclosure extends LocationSystem {
32 private _gatunek: string;
33 private _powierzchnia: number; // w hektarach
34 private _specimenCount: number;
35
36 constructor(numer: number, nazwa: string, gatunek: string, powierzchnia: number, specimenCount: number) {
37 // Generating ID for the enclosure
38 const id = DinosaurEnclosure.generateLocationId("wybieg", numer);
39 super(id, nazwa);
40
41 this._species = species;
42 this._powierzchnia = powierzchnia;
43 this._specimenCount = specimenCount;
44 }
45
46 // Implementation of abstract static method
47 static generateLocationId(typ: string, numer: number): string {
48 return `${typ.toUpperCase()}-${numer.toString().padStart(3, '0')}`;
49 }
50
51 // Implementation of abstract instance method
52 pobierzInformacjeOLokacji(): Record<string, any> {
53 return {
54 typ: "Dinosaur enclosure",
55 gatunek: this._gatunek,
56 powierzchnia: `${this._powierzchnia} ha`,
57 specimenCount: this._specimenCount,
58 populationDensity: `${(this._specimenCount / this._powierzchnia).toFixed(2)} spec./ha`
59 };
60 }
61
62 // Methods specific to this class
63 addSpecimen(): void {
64 this._specimenCount++;
65 console.log(`Added new specimen to enclosure ${this._name}. New count: ${this._specimenCount}`);
66 }
67
68 removeSpecimen(): boolean {
69 if (this._specimenCount > 0) {
70 this._specimenCount--;
71 console.log(`Removed specimen from enclosure ${this._name}. New count: ${this._specimenCount}`);
72 return true;
73 }
74
75 console.log(`Error: Cannot remove specimen from enclosure ${this._name} - no specimens.`);
76 return false;
77 }
78}
79
80class TouristAttraction extends LocationSystem {
81 private _attractionType: string;
82 private _capacity: number;
83 private _czasOczekiwania: number; // w minutach
84
85 constructor(numer: number, nazwa: string, attractionType: string, capacity: number) {
86 // Generating ID for the attraction
87 const id = TouristAttraction.generateLocationId("atrakcja", numer);
88 super(id, nazwa);
89
90 this._attractionType = attractionType;
91 this._capacity = capacity;
92 this._czasOczekiwania = 0;
93 }
94
95 // Implementation of abstract static method
96 static generateLocationId(typ: string, numer: number): string {
97 // Different ID format for attractions
98 return `${typ.substring(0, 3).toUpperCase()}${numer.toString().padStart(4, '0')}`;
99 }
100
101 // Implementation of abstract instance method
102 pobierzInformacjeOLokacji(): Record<string, any> {
103 return {
104 typ: "Atrakcja turystyczna",
105 category: this._attractionType,
106 capacity: this._capacity,
107 czasOczekiwania: `${this._czasOczekiwania} min`
108 };
109 }
110
111 // Methods specific to this class
112 aktualizujCzasOczekiwania(nowyOczekiwanieCzas: number): void {
113 this._czasOczekiwania = nowyOczekiwanieCzas;
114 console.log(`Zaktualizowano czas oczekiwania dla ${this._name}: ${this._czasOczekiwania} min`);
115 }
116}
117
118// Application
119function testLocationSystem() {
120 // Creating enclosures
121 const tRexEnclosure = new DinosaurEnclosure(1, "T-Rex Kingdom", "Tyrannosaurus Rex", 5, 1);
122 const raptorEnclosure = new DinosaurEnclosure(2, "Raptor Valley", "Velociraptor", 3, 6);
123
124 // Creating attractions
125 const visitorCenter = new TouristAttraction(101, "Genetics Center", "educational", 500);
126 const jeepSafari = new TouristAttraction(102, "Jurassic Safari", "ride", 50);
127
128 // Displaying reports
129 console.log(wybiegTRex.generateReport());
130 console.log(raptorEnclosure.generateReport());
131 console.log(visitorCenter.generateReport());
132 console.log(jeepSafari.generateReport());
133
134 // Performing operations
135 raptorEnclosure.addSpecimen();
136 raptorEnclosure.addSpecimen();
137 jeepSafari.aktualizujCzasOczekiwania(45);
138
139 // Checking updated data
140 console.log(raptorEnclosure.generateReport());
141 console.log(jeepSafari.generateReport());
142
143 // Demonstration of different ID formats generated by static method implementations
144 console.log("Enclosure ID format:", DinosaurEnclosure.generateLocationId("wybieg", 42));
145 console.log("Format ID atrakcji:", TouristAttraction.generateLocationId("atrakcja", 42));
146}
147
148testLocationSystem();In this exercise, we learned about two powerful TypeScript mechanisms:
Static properties and methods:
Abstract classes and methods:
By combining these concepts, we can create elegant, modular, and type-safe systems that are ideal for modeling complex relationships and processes, such as those in the Jurassic Park management system.
Dr. Henry Wu would summarize it as follows: "In nature, as in programming, there is beauty in structure and order. Static elements ensure consistency for the entire species, while abstract concepts form the basic blueprint that each organism must implement in its own way. Together they create perfect harmony - with proper safeguards, of course... in most cases."