JavaScript and TypeScript course Β· Module 6: TypeScript Basics
Extending interfaces
In this lesson9
Welcome back to Jurassic Park! Today Dr. Henry Wu will guide us through an advanced TypeScript technique that is the foundation of building scalable and flexible systems - extending interfaces. Just as scientists in the park extend the basic traits of dinosaurs with new genetic properties, in TypeScript we can extend basic interfaces with new functionality.
Basic mechanism of extending interfaces
Extending interfaces in TypeScript allows us to create new interfaces on the basis of existing ones, adding new properties or methods. We use the extends keyword:
1// Base interface for all creatures in the park
2interface Creature {
3 id: string;
4 name: string;
5 species: string;
6 age: number;
7 weight: number;
8}
9
10// Extending the base interface with additional properties for dinosaurs
11interface Dinosaur extends Creature {
12 originEra: string; // e.g. "Jurassic", "Cretaceous"
13 diet: "herbivore" | "carnivore" | "omnivore";
14 dangerous: boolean;
15}
16
17// Now the Dinosaur interface contains all fields from Creature plus its own
18const trex: Dinosaur = {
19 id: "TREX-01",
20 name: "Rex",
21 species: "Tyrannosaurus Rex",
22 age: 7,
23 weight: 7000,
24 originEra: "Cretaceous",
25 diet: "carnivore",
26 dangerous: true
27};Extending multiple interfaces
An interface can extend multiple other interfaces simultaneously, allowing complex types to be composed from smaller, specialized parts - similar to how scientists combine DNA from different species to create hybrids:
1// Interface defining predator abilities
2interface PredatorAbilities {
3 biteForce: number; // in PSI
4 runSpeed: number; // in km/h
5 huntingStyle: "ambush" | "chase" | "pack";
6}
7
8// Interface defining aquatic animal traits
9interface AquaticAnimal {
10 swimmingAbility: number; // 1 to 10
11 maxDepth: number; // in meters
12 saltWaterResistance: boolean;
13}
14
15// Interface for a marine predator combining both of the above interfaces
16interface MarinePredator extends PredatorAbilities, AquaticAnimal {
17 scientificName: string;
18}
19
20// Creating a dinosaur that is a marine predator
21const mosasaurus: MarinePredator & Dinosaur = {
22 // Properties from Creature
23 id: "MOSA-01",
24 name: "Mosa",
25 species: "Mosasaurus",
26 age: 15,
27 weight: 15000,
28
29 // Properties from Dinosaur
30 originEra: "Cretaceous",
31 diet: "carnivore",
32 dangerous: true,
33
34 // Properties from PredatorAbilities
35 biteForce: 18000,
36 runSpeed: 0, // doesn't run, swims
37 huntingStyle: "ambush",
38
39 // Properties from AquaticAnimal
40 swimmingAbility: 9,
41 maxDepth: 500,
42 saltWaterResistance: true,
43
44 // Properties from MarinePredator
45 scientificName: "Mosasaurus hoffmannii"
46};
47
48// Demonstration of accessing different properties
49function analyzeMarinePredator(dino: MarinePredator & Dinosaur): void {
50 console.log(`Analyzing: ${dino.name} (${dino.species})`);
51 console.log(`Bite force: ${dino.biteForce} PSI`);
52 console.log(`Can dive to depth: ${dino.maxDepth} meters`);
53 console.log(`Visitor threat: ${dino.dangerous ? "HIGH" : "LOW"}`);
54}
55
56analyzeMarinePredator(mosasaurus);Extending interfaces vs class inheritance
Extending interfaces is similar to class inheritance, but has several key differences. In Jurassic Park, this can be compared to the difference between "description of an ideal dinosaur" (interface) and "an actual dinosaur" (class):
1// Interface for security systems
2interface SecuritySystem {
3 name: string;
4 securityLevel: number;
5 isActive: boolean;
6
7 activate(): void;
8 deactivate(): void;
9 test(): boolean;
10}
11
12// Class implementing the interface
13class ElectronicFence implements SecuritySystem {
14 name: string;
15 securityLevel: number;
16 isActive: boolean;
17 voltage: number; // additional property specific to this class
18
19 constructor(name: string, securityLevel: number, voltage: number) {
20 this.name = name;
21 this.securityLevel = securityLevel;
22 this.isActive = false;
23 this.voltage = voltage;
24 }
25
26 activate(): void {
27 this.isActive = true;
28 console.log(`System ${this.name} activated with voltage ${this.voltage}V`);
29 }
30
31 deactivate(): void {
32 this.isActive = false;
33 console.log(`System ${this.name} deactivated`);
34 }
35
36 test(): boolean {
37 console.log(`Testing system ${this.name}...`);
38 return Math.random() > 0.1; // 90% chance of successful test
39 }
40
41 // Method specific to this class
42 increaseVoltage(by: number): void {
43 this.voltage += by;
44 console.log(`Voltage increased to ${this.voltage}V`);
45 }
46}
47
48// Extended version of SecuritySystem for more advanced systems
49interface AdvancedSecuritySystem extends SecuritySystem {
50 backupPower: boolean;
51 videoMonitoring: boolean;
52 alarmNotifications: boolean;
53
54 runSecurityProcedure(threatLevel: number): void;
55}
56
57// Class implementing the extended interface
58class IntegratedSecuritySystem implements AdvancedSecuritySystem {
59 name: string;
60 securityLevel: number;
61 isActive: boolean;
62 backupPower: boolean;
63 videoMonitoring: boolean;
64 alarmNotifications: boolean;
65
66 constructor(name: string, securityLevel: number) {
67 this.name = name;
68 this.securityLevel = securityLevel;
69 this.isActive = false;
70 this.backupPower = true;
71 this.videoMonitoring = true;
72 this.alarmNotifications = true;
73 }
74
75 activate(): void {
76 this.isActive = true;
77 console.log(`Integrated system ${this.name} activated`);
78 }
79
80 deactivate(): void {
81 this.isActive = false;
82 console.log(`Integrated system ${this.name} deactivated`);
83 }
84
85 test(): boolean {
86 console.log(`Testing integrated system ${this.name}...`);
87 const powerTest = Math.random() > 0.05;
88 const monitoringTest = Math.random() > 0.05;
89 const notificationsTest = Math.random() > 0.05;
90
91 return powerTest && monitoringTest && notificationsTest;
92 }
93
94 runSecurityProcedure(threatLevel: number): void {
95 console.log(`Activating security procedure level ${threatLevel} for system ${this.name}`);
96 this.activate();
97
98 if (threatLevel >= 4) {
99 console.log("WARNING: High threat level! Activating all security measures!");
100 }
101 }
102}
103
104// Usage demonstration
105const trexFence = new ElectronicFence("T-Rex Enclosure", 5, 10000);
106trexFence.activate();
107trexFence.increaseVoltage(2000);
108
109const integratedParkSystem = new IntegratedSecuritySystem("Main System", 5);
110integratedParkSystem.runSecurityProcedure(4);Extending and implementing generic interfaces
Generic interfaces are especially powerful when modeling complex systems, such as monitoring different types of data in the park:
1// Generic interface for a monitoring system
2interface MonitoringSystemType<T> {
3 identyfikator: string;
4 typDanych: string;
5 aktualneDane: T;
6 historyczneDane: T[];
7
8 zapisz(dane: T): void;
9 pobierzOstatnie(n: number): T[];
10 analizujTrend(): string;
11}
12
13// Sample data type for environmental monitoring
14interface EnvironmentalData {
15 temperatura: number;
16 humidity: number;
17 poziomCO2: number;
18 sunExposure: number;
19 pressure: number;
20 czasPomiaru: Date;
21}
22
23// Implementation of monitoring system for a specific data type
24class EnvironmentalMonitoring implements MonitoringSystemType<EnvironmentalData> {
25 identyfikator: string;
26 typDanych: string;
27 aktualneDane: EnvironmentalData;
28 historyczneDane: EnvironmentalData[];
29
30 constructor(identyfikator: string) {
31 this.identyfikator = identyfikator;
32 this.typDanych = "environmental";
33 this.aktualneDane = {
34 temperatura: 0,
35 humidity: 0,
36 poziomCO2: 0,
37 sunExposure: 0,
38 pressure: 0,
39 czasPomiaru: new Date()
40 };
41 this.historyczneDane = [];
42 }
43
44 zapisz(dane: EnvironmentalData): void {
45 this.aktualneDane = dane;
46 this.historyczneDane.push(dane);
47 console.log(`Saved new data for monitor ${this.identyfikator} at ${dane.czasPomiaru.toISOString()}`);
48 }
49
50 pobierzOstatnie(n: number): EnvironmentalData[] {
51 return this.historyczneDane.slice(-n);
52 }
53
54 analizujTrend(): string {
55 if (this.historyczneDane.length < 2) {
56 return "Not enough data to analyze trend";
57 }
58
59 const ostatnie = this.historyczneDane[this.historyczneDane.length - 1];
60 const przedostatnie = this.historyczneDane[this.historyczneDane.length - 2];
61
62 let trendy = [];
63
64 if (ostatnie.temperatura > przedostatnie.temperatura) {
65 trendy.push("Temperature: RISING");
66 } else if (ostatnie.temperatura < przedostatnie.temperatura) {
67 trendy.push("Temperature: FALLING");
68 } else {
69 trendy.push("Temperature: STABLE");
70 }
71
72 if (ostatnie.humidity > przedostatnie.humidity) {
73 trendy.push("Humidity: RISING");
74 } else if (ostatnie.humidity < przedostatnie.humidity) {
75 trendy.push("Humidity: FALLING");
76 } else {
77 trendy.push("Humidity: STABLE");
78 }
79
80 return trendy.join(", ");
81 }
82
83 // Method specific to environmental monitoring
84 temperatureWarning(): boolean {
85 return this.aktualneDane.temperatura > 35 || this.aktualneDane.temperatura < 15;
86 }
87}
88
89// Extending the base interface with specific functions for dinosaur monitoring
90interface DinosaurMonitoringSystem<T> extends MonitoringSystemType<T> {
91 idDinosaura: string;
92 species: string;
93 progiAlarmowe: Partial<T>;
94
95 checkAlarmThresholds(): boolean;
96 sendAlertIfNeeded(): void;
97}
98
99// Data type for dinosaur health monitoring
100interface DinosaurHealthData {
101 bodyTemperature: number;
102 heartRate: number;
103 bloodPressure: [number, number]; // [systolic, diastolic]
104 poziomStresu: number; // 1-10
105 activityLevel: number; // 1-10
106 czasPomiaru: Date;
107}
108
109// Implementation of specific monitoring system for dinosaurs
110class DinosaurHealthMonitoring implements DinosaurMonitoringSystem<DinosaurHealthData> {
111 identyfikator: string;
112 typDanych: string;
113 aktualneDane: DinosaurHealthData;
114 historyczneDane: DinosaurHealthData[];
115 idDinosaura: string;
116 species: string;
117 progiAlarmowe: Partial<DinosaurHealthData>;
118
119 constructor(identyfikator: string, idDinosaura: string, species: string) {
120 this.identyfikator = identyfikator;
121 this.typDanych = "health";
122 this.aktualneDane = {
123 bodyTemperature: 0,
124 heartRate: 0,
125 bloodPressure: [0, 0],
126 poziomStresu: 0,
127 activityLevel: 0,
128 czasPomiaru: new Date()
129 };
130 this.historyczneDane = [];
131 this.idDinosaura = idDinosaura;
132 this.species = species;
133
134 // Setting alarm thresholds based on species
135 if (species === "Tyrannosaurus") {
136 this.progiAlarmowe = {
137 bodyTemperature: 39, // Above this temperature - alarm
138 heartRate: 95, // Above this heart rate - alarm
139 poziomStresu: 7 // Above this stress level - alarm
140 };
141 } else if (species === "Velociraptor") {
142 this.progiAlarmowe = {
143 bodyTemperature: 40,
144 heartRate: 120,
145 poziomStresu: 8
146 };
147 } else {
148 // Default thresholds for other species
149 this.progiAlarmowe = {
150 bodyTemperature: 38,
151 heartRate: 100,
152 poziomStresu: 7
153 };
154 }
155 }
156
157 zapisz(dane: DinosaurHealthData): void {
158 this.aktualneDane = dane;
159 this.historyczneDane.push(dane);
160 console.log(`Saved new health data for dinosaur ${this.idDinosaura} (${this.species})`);
161
162 // Check if values exceed thresholds
163 this.sendAlertIfNeeded();
164 }
165
166 pobierzOstatnie(n: number): DinosaurHealthData[] {
167 return this.historyczneDane.slice(-n);
168 }
169
170 analizujTrend(): string {
171 if (this.historyczneDane.length < 2) {
172 return "Not enough data to analyze trend";
173 }
174
175 return "Health trend analysis (sample implementation)";
176 }
177
178 checkAlarmThresholds(): boolean {
179 let thresholdExceeded = false;
180
181 if (this.progiAlarmowe.bodyTemperature &&
182 this.aktualneDane.bodyTemperature > this.progiAlarmowe.bodyTemperature) {
183 console.log(`ALARM: Dinosaur ${this.idDinosaura} body temperature exceeds threshold!`);
184 thresholdExceeded = true;
185 }
186
187 if (this.progiAlarmowe.heartRate &&
188 this.aktualneDane.heartRate > this.progiAlarmowe.heartRate) {
189 console.log(`ALARM: Dinosaur ${this.idDinosaura} heart rate exceeds threshold!`);
190 thresholdExceeded = true;
191 }
192
193 if (this.progiAlarmowe.poziomStresu &&
194 this.aktualneDane.poziomStresu > this.progiAlarmowe.poziomStresu) {
195 console.log(`ALARM: Dinosaur ${this.idDinosaura} stress level exceeds threshold!`);
196 thresholdExceeded = true;
197 }
198
199 return thresholdExceeded;
200 }
201
202 sendAlertIfNeeded(): void {
203 if (this.checkAlarmThresholds()) {
204 console.log(`Sending alert to veterinary team for dinosaur ${this.idDinosaura}!`);
205 }
206 }
207
208 // Method specific to dinosaur health monitoring
209 recommendIntervention(): string {
210 let rekomendacje = [];
211
212 if (this.aktualneDane.bodyTemperature > this.progiAlarmowe.bodyTemperature!) {
213 rekomendacje.push("Lower body temperature");
214 }
215
216 if (this.aktualneDane.poziomStresu > this.progiAlarmowe.poziomStresu!) {
217 rekomendacje.push("Administer sedatives");
218 }
219
220 if (this.aktualneDane.activityLevel < 3) {
221 rekomendacje.push("Check energy level and possibly increase food portion");
222 }
223
224 return rekomendacje.length > 0
225 ? `Recommended actions: ${rekomendacje.join(", ")}`
226 : "No recommendations - parameters within normal range";
227 }
228}
229
230// Example usage
231const monitorPaddockTRex = new EnvironmentalMonitoring("ENV-TREX-01");
232monitorPaddockTRex.zapisz({
233 temperatura: 28.5,
234 humidity: 75,
235 poziomCO2: 420,
236 sunExposure: 80,
237 pressure: 1013,
238 czasPomiaru: new Date()
239});
240
241const monitorRexy = new DinosaurHealthMonitoring("HEALTH-TREX-01", "TREX-01", "Tyrannosaurus");
242monitorRexy.zapisz({
243 bodyTemperature: 38.2,
244 heartRate: 72,
245 bloodPressure: [135, 85],
246 poziomStresu: 3,
247 activityLevel: 4,
248 czasPomiaru: new Date()
249});
250
251// Simulating a health problem
252monitorRexy.zapisz({
253 bodyTemperature: 40.1, // Fever!
254 heartRate: 98, // Elevated heart rate
255 bloodPressure: [155, 95], // High blood pressure
256 poziomStresu: 8, // High stress level
257 activityLevel: 2, // Low activity
258 czasPomiaru: new Date()
259});
260
261console.log(monitorRexy.recommendIntervention());Interface declaration merging
One of the unique features of TypeScript is the ability to merge multiple declarations of the same interface - similar to how Jurassic Park scientists can update their handling protocols as they gain new information:
1// Initial definition of the security protocol
2interface SecurityProtocolType {
3 id: string;
4 nazwa: string;
5 threatLevel: 1 | 2 | 3 | 4 | 5;
6 proceduraEwakuacji: string;
7}
8
9// Later extension of the same interface with additional information
10interface SecurityProtocolType {
11 personelOdpowiedzialny: string[];
12 czasReakcji: number; // in minutes
13}
14
15// Even later extension with communication procedures
16interface SecurityProtocolType {
17 communicationChannels: string[];
18 alarmMessage: string;
19}
20
21// Now SecurityProtocolType has all fields from all three declarations
22const protocolAlpha: SecurityProtocolType = {
23 id: "PROTO-ALFA",
24 nazwa: "Protocol Alpha - Predator Escape",
25 threatLevel: 5,
26 proceduraEwakuacji: "Immediate evacuation of all public areas to safety bunkers",
27 personelOdpowiedzialny: ["Security manager", "Park manager", "Head trainer"],
28 czasReakcji: 5,
29 communicationChannels: ["Radio", "PA System", "Audible alarm", "SMS notifications"],
30 alarmMessage: "ATTENTION! PROTOCOL ALPHA! This is not a drill. Please immediately proceed to the nearest shelter."
31};
32
33// Function to display the protocol
34function displayProtocol(protocol: SecurityProtocolType): void {
35 console.log(`=== ${protocol.nazwa.toUpperCase()} ===`);
36 console.log(`ID: ${protocol.id}`);
37 console.log(`Threat level: ${protocol.threatLevel}`);
38 console.log(`Response time: ${protocol.czasReakcji} minutes`);
39 console.log(`\nEvacuation procedure:\n${protocol.proceduraEwakuacji}`);
40 console.log(`\nResponsible personnel:`);
41 protocol.personelOdpowiedzialny.forEach(osoba => console.log(`- ${osoba}`));
42 console.log(`\nCommunication channels: ${protocol.communicationChannels.join(", ")}`);
43 console.log(`\nAlarm message: "${protocol.alarmMessage}"`);
44}
45
46displayProtocol(protocolAlpha);Structural compatibility of interfaces
TypeScript uses structural compatibility, meaning types are compatible if they have the same structure, regardless of their names. It's like in Jurassic Park - if a dinosaur behaves like a T-Rex and looks like a T-Rex, from the security system's perspective it should be treated like a T-Rex:
1// Definition of a predatory dinosaur interface
2interface PredatoryDinosaur {
3 id: string;
4 nazwa: string;
5 biteForce: number;
6 runSpeed: number;
7 poziomAgresji: number;
8}
9
10// Completely separate definition, with no formal relationship to the above interface
11interface DangerousCreature {
12 id: string;
13 nazwa: string;
14 biteForce: number;
15 runSpeed: number;
16 poziomAgresji: number;
17 // Additional field that does not interfere with compatibility
18 pochodzenie?: string;
19}
20
21function assessThreat(dino: PredatoryDinosaur): number {
22 return (dino.biteForce / 1000) + (dino.runSpeed / 10) + dino.poziomAgresji;
23}
24
25// Creating an object conforming to DangerousCreature
26const indominus: DangerousCreature = {
27 id: "IND-01",
28 nazwa: "Indominus Rex",
29 biteForce: 15000,
30 runSpeed: 48,
31 poziomAgresji: 10,
32 pochodzenie: "Laboratory"
33};
34
35// Even though indominus is of type DangerousCreature, it can be used where
36// PredatoryDinosaur is expected, thanks to structural compatibility
37const threatLevel = assessThreat(indominus);
38console.log(`Threat level for ${indominus.nazwa}: ${threatLevel}`);
39
40// Function expecting an object conforming to DangerousCreature
41function describeThreat(creature: DangerousCreature): string {
42 let opis = `${creature.nazwa} is an extremely dangerous creature.\n`;
43 opis += `Bite force: ${creature.biteForce} PSI\n`;
44 opis += `Running speed: ${creature.runSpeed} km/h\n`;
45 opis += `Aggression level: ${creature.poziomAgresji}/10\n`;
46
47 if (creature.pochodzenie) {
48 opis += `Created in: ${creature.pochodzenie}`;
49 }
50
51 return opis;
52}
53
54// Creating an object conforming to PredatoryDinosaur
55const velociraptor: PredatoryDinosaur = {
56 id: "VEL-01",
57 nazwa: "Blue",
58 biteForce: 4000,
59 runSpeed: 65,
60 poziomAgresji: 7
61};
62
63// We can use an object of type PredatoryDinosaur in a function expecting DangerousCreature
64// It works because velociraptor has all the required properties
65const opisRaptora = describeThreat(velociraptor);
66console.log(opisRaptora);Interfaces vs abstract types
Comparing interfaces with abstract types (abstract classes) in the context of Jurassic Park:
1// Interface for security systems
2interface SecuritySystemInterface {
3 nazwa: string;
4 aktywuj(): void;
5 dezaktywuj(): void;
6 testuj(): boolean;
7}
8
9// Abstract class for security systems
10abstract class SecuritySystemAbstractClass {
11 constructor(public nazwa: string, protected securityLevel: number) {}
12
13 abstract aktywuj(): void;
14 abstract dezaktywuj(): void;
15
16 // Shared implementation for all derived classes
17 testuj(): boolean {
18 console.log(`Testing system ${this.nazwa}...`);
19 return Math.random() > 0.1; // 90% chance of success
20 }
21
22 // Method available to all derived classes
23 pobierzPoziom(): number {
24 return this.securityLevel;
25 }
26}
27
28// Interface implementation
29class SkaneryRuchu implements SecuritySystemInterface {
30 constructor(public nazwa: string) {}
31
32 aktywuj(): void {
33 console.log(`Motion scanners ${this.nazwa} activated`);
34 }
35
36 dezaktywuj(): void {
37 console.log(`Motion scanners ${this.nazwa} deactivated`);
38 }
39
40 testuj(): boolean {
41 console.log(`Testing motion scanners ${this.nazwa}...`);
42 return true;
43 }
44}
45
46// Extending the abstract class
47class ElektroniczneOgrodzenie extends SecuritySystemAbstractClass {
48 private voltage: number;
49
50 constructor(nazwa: string, securityLevel: number, voltage: number) {
51 super(nazwa, securityLevel);
52 this.voltage = voltage;
53 }
54
55 aktywuj(): void {
56 console.log(`Electric fence ${this.nazwa} activated with voltage ${this.voltage}V`);
57 }
58
59 dezaktywuj(): void {
60 console.log(`Electric fence ${this.nazwa} deactivated`);
61 }
62
63 increaseVoltage(o: number): void {
64 this.voltage += o;
65 console.log(`Voltage of fence ${this.nazwa} increased to ${this.voltage}V`);
66 }
67}
68
69// Using both approaches
70const skanery = new SkaneryRuchu("T-Rex Zone");
71skanery.aktywuj();
72skanery.testuj();
73
74const ogrodzenie = new ElektroniczneOgrodzenie("Eastern paddock", 4, 10000);
75ogrodzenie.aktywuj();
76ogrodzenie.increaseVoltage(2000);
77console.log(`Fence security level: ${ogrodzenie.pobierzPoziom()}`);Key differences:
| Feature | Interface | Abstract class |
|---|---|---|
| Implementation | Definition only, no implementation | Can contain method implementations |
| Inheritance | Class can implement multiple interfaces | Class can only inherit from one abstract class |
| Constructor | No constructor | Can have a constructor |
| Runtime code | Interfaces generate no JavaScript code | Abstract classes generate JavaScript code |
| Access modifiers | Cannot use modifiers (private, protected) | Can use access modifiers |
| Use | Defining a contract | Defining base functionality with partial implementation |
Practical application of interface extension
Let's now see a comprehensive example illustrating how interface extension can be used in the Jurassic Park management system:
1// Base interface for all entities in the park
2interface BazaParkuJurajskiego {
3 id: string;
4 nazwa: string;
5 dataDodania: Date;
6 ostatniaAktualizacja: Date;
7}
8
9// Extension for living creatures
10interface ParkLivingCreatures extends BazaParkuJurajskiego {
11 wiek: number;
12 gender: "male" | "female";
13 statusZdrowia: "excellent" | "good" | "needs attention" | "sick" | "critical";
14 historiaZdrowia: {
15 data: Date;
16 opis: string;
17 procedura?: string;
18 }[];
19}
20
21// Extension for dinosaurs
22interface DinosaurParku extends ParkLivingCreatures {
23 species: string;
24 eraPochodzenia: string;
25 dieta: "herbivore" | "carnivore" | "omnivore";
26 dangerLevel: 1 | 2 | 3 | 4 | 5;
27 lokalizacja: string; // Enclosure ID
28 cechy: {
29 height: number;
30 length: number;
31 waga: number;
32 maxSpeed: number;
33 biteForce?: number; // Only for predators
34 };
35}
36
37// Extension for employees
38interface PracownikParku extends ParkLivingCreatures {
39 firstName: string;
40 nazwisko: string;
41 stanowisko: string;
42 accessLevel: 1 | 2 | 3 | 4 | 5;
43 departments: string[];
44 adres: {
45 ulica: string;
46 miasto: string;
47 kodPocztowy: string;
48 kraj: string;
49 };
50 kontakt: {
51 telefon: string;
52 email: string;
53 };
54}
55
56// Interface for park locations
57interface LokalizacjaParku extends BazaParkuJurajskiego {
58 typ: "enclosure" | "building" | "attraction" | "laboratory" | "public zone";
59 coordinates: {
60 width: number;
61 length: number;
62 };
63 powierzchnia: number; // in square meters
64 capacity: number;
65 accessLevel: 1 | 2 | 3 | 4 | 5;
66 securitySystems: string[]; // List of security system IDs
67 status: "active" | "inactive" | "under maintenance" | "under construction";
68}
69
70// Interface for dinosaur enclosures
71interface WybiegDinosaurs extends LokalizacjaParku {
72 residents: string[]; // List of dinosaur IDs
73 environmentType: "forest" | "savanna" | "swamp" | "mountains" | "coast" | "water";
74 specjalneWymagania?: string[];
75 cechy: {
76 heightOgrodzenia: number;
77 fenceVoltage?: number; // For predators
78 scannersCount: number;
79 liczbaKamer: number;
80 };
81}
82
83// Interface for laboratories
84interface LaboratoriumParku extends LokalizacjaParku {
85 specjalizacja: "genetics" | "behavioral" | "veterinary" | "microbiology";
86 equipment: string[];
87 personel: string[]; // List of employee IDs
88 projektyBadawcze: {
89 id: string;
90 nazwa: string;
91 start: Date;
92 koniec?: Date;
93 status: "planned" | "in progress" | "completed" | "suspended";
94 }[];
95}
96
97// Park management system
98class ParkJurajskiSystem {
99 private dinosaury: Map<string, DinosaurParku> = new Map();
100 private pracownicy: Map<string, PracownikParku> = new Map();
101 private lokalizacje: Map<string, LokalizacjaParku> = new Map();
102 private laboratorium: Map<string, LaboratoriumParku> = new Map();
103 private wybiegi: Map<string, WybiegDinosaurs> = new Map();
104
105 // Methods for dinosaurs
106 dodajDinosaura(dino: DinosaurParku): void {
107 this.dinosaury.set(dino.id, dino);
108 console.log(`Added dinosaur: ${dino.nazwa} (${dino.species})`);
109
110 // Update enclosure
111 if (dino.lokalizacja && this.wybiegi.has(dino.lokalizacja)) {
112 const wybieg = this.wybiegi.get(dino.lokalizacja)!;
113 wybieg.residents.push(dino.id);
114 this.wybiegi.set(wybieg.id, wybieg);
115 }
116 }
117
118 aktualizujStatusZdrowiaDinosaura(id: string, nowyStatus: DinosaurParku["statusZdrowia"], opis: string): void {
119 if (!this.dinosaury.has(id)) {
120 console.error(`Dinosaur with ID ${id} does not exist`);
121 return;
122 }
123
124 const dino = this.dinosaury.get(id)!;
125 const staryStatus = dino.statusZdrowia;
126 dino.statusZdrowia = nowyStatus;
127 dino.ostatniaAktualizacja = new Date();
128
129 // Add entry to health history
130 dino.historiaZdrowia.push({
131 data: new Date(),
132 opis: `Health status changed from "${staryStatus}" to "${nowyStatus}". ${opis}`
133 });
134
135 this.dinosaury.set(id, dino);
136 console.log(`Updated health status of dinosaur ${dino.nazwa} to: ${nowyStatus}`);
137 }
138
139 // Methods for employees
140 dodajPracownika(pracownik: PracownikParku): void {
141 this.pracownicy.set(pracownik.id, pracownik);
142 console.log(`Added employee: ${pracownik.firstName} ${pracownik.nazwisko} (${pracownik.stanowisko})`);
143 }
144
145 // Methods for locations
146 dodajWybieg(wybieg: WybiegDinosaurs): void {
147 this.wybiegi.set(wybieg.id, wybieg);
148 this.lokalizacje.set(wybieg.id, wybieg);
149 console.log(`Added enclosure: ${wybieg.nazwa}`);
150 }
151
152 dodajLaboratorium(lab: LaboratoriumParku): void {
153 this.laboratorium.set(lab.id, lab);
154 this.lokalizacje.set(lab.id, lab);
155 console.log(`Added laboratory: ${lab.nazwa} (${lab.specjalizacja})`);
156 }
157
158 // Reporting methods
159 generujRaportParkuJurajskiego(): string {
160 const raport = [];
161 raport.push("=== JURASSIC PARK STATUS REPORT ===");
162 raport.push(`Report date: ${new Date().toLocaleString()}`);
163 raport.push(`\nGeneral statistics:`);
164 raport.push(`- Number of dinosaurs: ${this.dinosaury.size}`);
165 raport.push(`- Number of employees: ${this.pracownicy.size}`);
166 raport.push(`- Number of locations: ${this.lokalizacje.size}`);
167 raport.push(` - Enclosures: ${this.wybiegi.size}`);
168 raport.push(` - Laboratories: ${this.laboratorium.size}`);
169
170 // Dinosaur statistics
171 const statystykiZdrowia = {
172 "excellent": 0,
173 "good": 0,
174 "needs attention": 0,
175 "sick": 0,
176 "critical": 0
177 };
178
179 let predatorsCount = 0;
180
181 for (const dino of this.dinosaury.values()) {
182 statystykiZdrowia[dino.statusZdrowia]++;
183 if (dino.dieta === "carnivore") {
184 predatorsCount++;
185 }
186 }
187
188 raport.push(`\nDinosaur health statistics:`);
189 for (const [status, liczba] of Object.entries(statystykiZdrowia)) {
190 raport.push(`- ${status}: ${liczba}`);
191 }
192
193 raport.push(`Predators: ${predatorsCount}`);
194 raport.push(`Herbivores: ${this.dinosaury.size - predatorsCount}`);
195
196 // Enclosure statistics
197 const wybiegiZeStatusem = {
198 "active": 0,
199 "inactive": 0,
200 "under maintenance": 0,
201 "under construction": 0
202 };
203
204 for (const wybieg of this.wybiegi.values()) {
205 wybiegiZeStatusem[wybieg.status]++;
206 }
207
208 raport.push(`\nEnclosure statistics:`);
209 for (const [status, liczba] of Object.entries(wybiegiZeStatusem)) {
210 raport.push(`- ${status}: ${liczba}`);
211 }
212
213 return raport.join("\n");
214 }
215}
216
217// Example usage
218const park = new ParkJurajskiSystem();
219
220// Adding an enclosure
221park.dodajWybieg({
222 id: "WYB-001",
223 nazwa: "T-Rex Paddock",
224 dataDodania: new Date(2020, 0, 15),
225 ostatniaAktualizacja: new Date(),
226 typ: "enclosure",
227 coordinates: {
228 width: 20.123,
229 length: -75.456
230 },
231 powierzchnia: 50000, // 5 hectares
232 capacity: 2,
233 accessLevel: 5,
234 securitySystems: ["SEC-001", "SEC-002", "SEC-003"],
235 status: "active",
236 residents: [],
237 environmentType: "forest",
238 cechy: {
239 heightOgrodzenia: 8,
240 fenceVoltage: 10000,
241 scannersCount: 24,
242 liczbaKamer: 36
243 }
244});
245
246// Adding a laboratory
247park.dodajLaboratorium({
248 id: "LAB-001",
249 nazwa: "Dr. Wu's Genetics Laboratory",
250 dataDodania: new Date(2018, 5, 10),
251 ostatniaAktualizacja: new Date(),
252 typ: "laboratory",
253 coordinates: {
254 width: 20.124,
255 length: -75.458
256 },
257 powierzchnia: 2000,
258 capacity: 30,
259 accessLevel: 4,
260 securitySystems: ["SEC-101", "SEC-102"],
261 status: "active",
262 specjalizacja: "genetics",
263 equipment: ["DNA sequencer", "Sample isolators", "Cryogenic chamber"],
264 personel: [],
265 projektyBadawcze: [
266 {
267 id: "PROJ-001",
268 nazwa: "Strengthening resistance genes in Triceratops",
269 start: new Date(2022, 3, 15),
270 status: "in progress"
271 }
272 ]
273});
274
275// Adding a dinosaur
276park.dodajDinosaura({
277 id: "DINO-001",
278 nazwa: "Rexy",
279 dataDodania: new Date(2020, 5, 20),
280 ostatniaAktualizacja: new Date(),
281 wiek: 7,
282 gender: "female",
283 statusZdrowia: "good",
284 historiaZdrowia: [
285 {
286 data: new Date(2020, 5, 20),
287 opis: "Initial examination. All parameters within normal range."
288 },
289 {
290 data: new Date(2021, 2, 15),
291 opis: "Routine examination. Slightly elevated triglycerides.",
292 procedura: "Diet change to one richer in Omega-3 fatty acids"
293 }
294 ],
295 species: "Tyrannosaurus Rex",
296 eraPochodzenia: "Late Cretaceous",
297 dieta: "carnivore",
298 dangerLevel: 5,
299 lokalizacja: "WYB-001",
300 cechy: {
301 height: 4.5,
302 length: 12,
303 waga: 7200,
304 maxSpeed: 32,
305 biteForce: 12800
306 }
307});
308
309// Adding an employee
310park.dodajPracownika({
311 id: "EMP-001",
312 nazwa: "Employee",
313 dataDodania: new Date(2019, 1, 10),
314 ostatniaAktualizacja: new Date(),
315 wiek: 42,
316 gender: "male",
317 statusZdrowia: "excellent",
318 historiaZdrowia: [
319 {
320 data: new Date(2022, 1, 5),
321 opis: "Routine periodic examination. All parameters within normal range."
322 }
323 ],
324 firstName: "Owen",
325 nazwisko: "Grady",
326 stanowisko: "Raptor Trainer",
327 accessLevel: 4,
328 departments: ["Behavioral Research", "Animal Care"],
329 adres: {
330 ulica: "Bungalow 12",
331 miasto: "Isla Nublar",
332 kodPocztowy: "00000",
333 kraj: "Costa Rica"
334 },
335 kontakt: {
336 telefon: "+1-555-123-4567",
337 email: "owen.grady@jurassicworld.com"
338 }
339});
340
341// Updating dinosaur health status
342park.aktualizujStatusZdrowiaDinosaura(
343 "DINO-001",
344 "needs attention",
345 "Noticed reduced appetite and slight fever."
346);
347
348// Generating report
349console.log(park.generujRaportParkuJurajskiego());Summary
Extending interfaces in TypeScript is a powerful technique that allows modeling complex type hierarchies and relationships between them. In the context of Jurassic Park, it enables us to precisely model various entities and systems, from dinosaurs and employees to advanced monitoring and security systems.
| Technique | Use in Jurassic Park |
|---|---|
| Basic interface extension | Hierarchy of creatures from base to specific species |
| Extending multiple interfaces | Creating hybrid types, like marine predators |
| Generic interface extension | Monitoring systems for different types of data |
| Interface declaration merging | Updating security protocols |
| Structural compatibility | Uniform treatment of different dinosaur types by security systems |
Dr. Wu summarizes: "Extending interfaces in TypeScript resembles our work on new dinosaur species. We start from a basic structure and extend it with new traits and functionality, creating increasingly specialized and complex entities. This flexibility and ability to reuse existing definitions means we can create cohesive, yet highly diverse systems - key in both genetics and programming."
Code for this lesson: index.ts
1// Extending interfaces in Jurassic Park
2console.log("Extending TypeScript interfaces\n");
3
4// ===========================================
5// 1. Basic extension
6// ===========================================
7console.log("=== BASIC EXTENSION ===");
8
9// Base interface
10interface Animal {
11 name: string;
12 age: number;
13 weight: number;
14}
15
16// Extending the interface
17interface Dinosaur extends Animal {
18 species: string;
19 period: "Triassic" | "Jurassic" | "Cretaceous";
20 diet: "carnivore" | "herbivore" | "omnivore";
21}
22
23const trex: Dinosaur = {
24 name: "Rexy",
25 age: 5,
26 weight: 8000,
27 species: "Tyrannosaurus Rex",
28 period: "Cretaceous",
29 diet: "carnivore"
30};
31
32console.log("T-Rex:", trex);
33
34// ===========================================
35// 2. Multiple extension
36// ===========================================
37console.log("\n=== MULTIPLE EXTENSION ===");
38
39interface HasName {
40 name: string;
41}
42
43interface HasWeight {
44 weight: number;
45}
46
47interface HasDiet {
48 diet: "carnivore" | "herbivore" | "omnivore";
49}
50
51// Extending multiple interfaces
52interface CompleteDinosaur extends HasName, HasWeight, HasDiet {
53 species: string;
54 dangerLevel: number;
55}
56
57const velociraptor: CompleteDinosaur = {
58 name: "Blue",
59 weight: 350,
60 diet: "carnivore",
61 species: "Velociraptor",
62 dangerLevel: 8
63};
64
65console.log("Velociraptor:", velociraptor);
66
67// ===========================================
68// 3. Property overriding
69// ===========================================
70console.log("\n=== PROPERTY OVERRIDING ===");
71
72interface BasicDinosaur {
73 id: string | number;
74 name: string;
75}
76
77// Narrowing type when extending
78interface SpecificDinosaur extends BasicDinosaur {
79 id: string; // narrows the type from string | number to string
80 trackingCode: string;
81}
82
83const trackedDino: SpecificDinosaur = {
84 id: "DINO-001", // must be a string
85 name: "Rex",
86 trackingCode: "TR-XYZ-123"
87};
88
89console.log("Tracked dinosaur:", trackedDino);
90
91// ===========================================
92// 4. Extending with additional methods
93// ===========================================
94console.log("\n=== EXTENDING WITH METHODS ===");
95
96interface LivingBeing {
97 name: string;
98 eat(amount: number): void;
99 sleep(hours: number): void;
100}
101
102interface Predator extends LivingBeing {
103 hunt(prey: string): string;
104 roar(): string;
105 attackPower: number;
106}
107
108class TyrannosaurusRex implements Predator {
109 name: string;
110 attackPower: number;
111 energy: number = 100;
112
113 constructor(name: string) {
114 this.name = name;
115 this.attackPower = 95;
116 }
117
118 eat(amount: number): void {
119 this.energy += amount * 2;
120 if (this.energy > 100) this.energy = 100;
121 console.log(`${this.name} ate ${amount}kg. Energy: ${this.energy}`);
122 }
123
124 sleep(hours: number): void {
125 this.energy += hours * 10;
126 if (this.energy > 100) this.energy = 100;
127 console.log(`${this.name} slept ${hours}h. Energy: ${this.energy}`);
128 }
129
130 hunt(prey: string): string {
131 this.energy -= 20;
132 return `${this.name} hunts ${prey}! Energy: ${this.energy}`;
133 }
134
135 roar(): string {
136 this.energy -= 5;
137 return `${this.name} roars: ROAAAAAR! (${this.attackPower} power)`;
138 }
139}
140
141const rexy = new TyrannosaurusRex("Rexy");
142console.log(rexy.roar());
143console.log(rexy.hunt("Triceratops"));
144rexy.eat(50);
145
146// ===========================================
147// 5. Chain extending
148// ===========================================
149console.log("\n=== CHAIN EXTENDING ===");
150
151interface Entity {
152 id: string;
153}
154
155interface LivingEntity extends Entity {
156 isAlive: boolean;
157}
158
159interface BreathingEntity extends LivingEntity {
160 oxygenLevel: number;
161}
162
163interface IntelligentEntity extends BreathingEntity {
164 intelligenceLevel: number;
165 canCommunicate: boolean;
166}
167
168const velociRaptor: IntelligentEntity = {
169 id: "VR-001",
170 isAlive: true,
171 oxygenLevel: 98,
172 intelligenceLevel: 85,
173 canCommunicate: true
174};
175
176console.log("Intelligent entity:", velociRaptor);
177
178// ===========================================
179// 6. Generics with extension
180// ===========================================
181console.log("\n=== GENERICS WITH EXTENSION ===");
182
183interface Trackable {
184 trackingId: string;
185 lastSeen: Date;
186}
187
188interface DataEntry extends Trackable {
189 data: any;
190}
191
192interface DinosaurEntry<T> extends DataEntry {
193 dinosaurData: T;
194}
195
196interface TRexData {
197 species: "Tyrannosaurus Rex";
198 teethCount: number;
199 biteForce: number;
200}
201
202const trexEntry: DinosaurEntry<TRexData> = {
203 trackingId: "TRK-001",
204 lastSeen: new Date(),
205 data: { notes: "Healthy specimen" },
206 dinosaurData: {
207 species: "Tyrannosaurus Rex",
208 teethCount: 60,
209 biteForce: 12800
210 }
211};
212
213console.log("T-Rex entry:", trexEntry);
214
215// ===========================================
216// 7. Extending with Index Signatures
217// ===========================================
218console.log("\n=== INDEX SIGNATURES ===");
219
220interface BaseConfig {
221 version: string;
222 enabled: boolean;
223}
224
225interface ExtendedConfig extends BaseConfig {
226 [key: string]: any; // additional dynamic properties
227}
228
229const parkConfig: ExtendedConfig = {
230 version: "2.0",
231 enabled: true,
232 maxCapacity: 10000,
233 securityLevel: "high",
234 maintenanceMode: false
235};
236
237console.log("Park config:", parkConfig);
238
239// ===========================================
240// 8. Extension vs Intersection
241// ===========================================
242console.log("\n=== EXTENDS VS INTERSECTION ===");
243
244// Extends (interfaces)
245interface A {
246 a: string;
247}
248
249interface B extends A {
250 b: number;
251}
252
253const objB: B = {
254 a: "value",
255 b: 123
256};
257
258// Intersection (types)
259type C = {
260 c: boolean;
261};
262
263type D = C & {
264 d: string[];
265};
266
267const objD: D = {
268 c: true,
269 d: ["item1", "item2"]
270};
271
272console.log("Interface extension:", objB);
273console.log("Type intersection:", objD);
274
275// ===========================================
276// 9. Practical example - Park System
277// ===========================================
278console.log("\n=== PARK SYSTEM ===");
279
280interface Identifiable {
281 id: string;
282}
283
284interface Timestamped {
285 createdAt: Date;
286 updatedAt: Date;
287}
288
289interface Locatable {
290 latitude: number;
291 longitude: number;
292}
293
294interface ParkEntity extends Identifiable, Timestamped, Locatable {
295 name: string;
296 status: "active" | "inactive" | "maintenance";
297}
298
299interface Paddock extends ParkEntity {
300 capacity: number;
301 currentOccupancy: number;
302 securityLevel: "low" | "medium" | "high" | "critical";
303}
304
305interface DinosaurPark extends ParkEntity {
306 paddocks: Paddock[];
307 totalDinosaurs: number;
308 isOpen: boolean;
309}
310
311const jurassicPark: DinosaurPark = {
312 id: "PARK-001",
313 createdAt: new Date("1993-06-11"),
314 updatedAt: new Date(),
315 latitude: 22.3964,
316 longitude: -159.5261,
317 name: "Jurassic Park",
318 status: "active",
319 paddocks: [],
320 totalDinosaurs: 0,
321 isOpen: false
322};
323
324console.log("Jurassic Park:", jurassicPark);
325
326// ===========================================
327// 10. Summary
328// ===========================================
329console.log("\n=== SUMMARY ===");
330console.log("extends - extending interfaces");
331console.log("Multiple extension - inheriting from several interfaces");
332console.log("Overriding - type narrowing");
333console.log("Methods - adding functionality");
334console.log("Chains - building a hierarchy");
335console.log("Generics - parametric extension");
336
337console.log("\nExtending interfaces mastered!");Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What happens when interface B extends interface A?
2. Can an interface in TypeScript extend multiple interfaces at the same time?
Hands-on tasks in the game
- Code editor
Create a base interface Animal and extend it to Dinosaur with additional fields era and diet.
- Horizontal ordering
Arrange the elements of interface extension in TypeScript: