Welcome to the advanced TypeScript type laboratory in Jurassic Park! Dr. Henry Wu is especially excited today, because we will be discussing two powerful TypeScript mechanisms that can be compared to his most ambitious genetic engineering projects: union and intersection types.
A union type allows a variable to accept values of one of several different types. It's a bit like a hybrid enclosure in the park that can accommodate different species of dinosaurs, but only one kind at a time.
We create a union type using the
| (pipe) operator:1// A variable can be of type string OR number
2let identyfikatorDinosaura: string | number;
3
4identyfikatorDinosaura = "TREX-01"; // Ok, string is allowed
5identyfikatorDinosaura = 12345; // Ok, number is allowed
6// identyfikatorDinosaura = true; // Error! boolean is not part of the union string | numberUnion types are extremely useful in many practical scenarios in Jurassic Park:
1// Function that can accept different types of identifiers
2function findDinosaur(id: string | number): void {
3 // We must check the type before performing type-specific operations
4 if (typeof id === "string") {
5 console.log(`Looking for dinosaur with code: ${id.toUpperCase()}`);
6 } else {
7 console.log(`Looking for dinosaur with number: ${id.toFixed(0)}`);
8 }
9}
10
11findDinosaur("RAPTOR-BLUE"); // "Looking for dinosaur with code: RAPTOR-BLUE"
12findDinosaur(42); // "Looking for dinosaur with number: 42"1// Function that can return a dinosaur or an error message
2function pobierzDaneDinosaura(id: string):
3 { nazwa: string; species: string; wiek: number } | string {
4
5 // Simulating a database
6 const bazaDanych = new Map([
7 ["TRex1", { nazwa: "Rex", species: "Tyrannosaurus", wiek: 7 }],
8 ["Rap1", { nazwa: "Blue", species: "Velociraptor", wiek: 4 }]
9 ]);
10
11 const dinosaur = bazaDanych.get(id);
12
13 if (dinosaur) {
14 return dinosaur; // Returns dinosaur object
15 } else {
16 return `Dinosaur with ID not found: ${id}`; // Returns error message
17 }
18}
19
20const wynik = pobierzDaneDinosaura("TRex1");
21// We need to check the type of the returned value
22if (typeof wynik === "string") {
23 console.log(`Error: ${wynik}`);
24} else {
25 console.log(`Found: ${wynik.nazwa}, species: ${wynik.species}`);
26}1// Function generating a report that can accept different filter parameters
2function generateDinosaurReport(
3 filtr?: { species?: string; minAge?: number; predators?: boolean }
4): string {
5 let raport = "Dinosaur report:\n";
6
7 if (filtr) {
8 raport += "Filters:";
9 if (filtr.species) raport += ` Species=${filtr.species}`;
10 if (filtr.minAge) raport += ` MinAge=${filtr.minAge}`;
11 if (filtr.predators !== undefined) raport += ` OnlyPredators=${filtr.predators}`;
12 } else {
13 raport += "No filters - showing all dinosaurs";
14 }
15
16 return raport;
17}
18
19console.log(generateDinosaurReport()); // Without filters
20console.log(generateDinosaurReport({ species: "Velociraptor" })); // With species filter
21console.log(generateDinosaurReport({ minAge: 5, predators: true })); // With multiple filtersDiscriminated unions are an advanced technique of using union types with a "discriminating property" that allows TypeScript to recognize which type we're dealing with. It's like the dinosaur identification system in the park, which immediately tells whether we're dealing with a herbivore or a predator.
1// Defining interfaces with the discriminating property "typ"
2interface Herbivore {
3 typ: "herbivore";
4 dieta: string[];
5 poziomAgresji: number; // 1-10
6}
7
8interface Predator {
9 typ: "predator";
10 ofiary: string[];
11 biteForce: number; // in PSI
12}
13
14// Type that is a union of two interfaces
15type Dinosaur = Herbivore | Predator;
16
17// Function using the discriminated union
18function manageDinosaur(dino: Dinosaur): void {
19 // We check the discriminating property
20 switch (dino.typ) {
21 case "herbivore":
22 // TypeScript knows that dino is of type Herbivore here
23 console.log(`Prepare fodder: ${dino.dieta.join(", ")}`);
24 if (dino.poziomAgresji > 7) {
25 console.log("Warning: Herbivore with high aggression level!");
26 }
27 break;
28 case "predator":
29 // TypeScript knows that dino is of type Predator here
30 console.log(`Prepare meat for: ${dino.ofiary.join(", ")}`);
31 if (dino.biteForce > 10000) {
32 console.log("Warning: Very dangerous predator!");
33 }
34 break;
35 }
36}
37
38// Usage examples
39const triceratops: Herbivore = {
40 typ: "herbivore",
41 dieta: ["ferns", "cycads", "conifers"],
42 poziomAgresji: 6
43};
44
45const tRex: Predator = {
46 typ: "predator",
47 ofiary: ["triceratops", "edmontosaurus", "pachycephalosaurus"],
48 biteForce: 12800
49};
50
51manageDinosaur(triceratops);
52manageDinosaur(tRex);An intersection type allows combining multiple types into one, more complex type. It's like Dr. Wu's experiments with combining DNA from different dinosaurs to create entirely new species with the traits of all "ingredients".
We create an intersection type using the
& operator:1// Defining interfaces of basic traits
2interface BasicDinosaur {
3 id: number;
4 nazwa: string;
5 species: string;
6 wiek: number;
7}
8
9interface PhysicalTraits {
10 height: number; // in meters
11 length: number; // in meters
12 waga: number; // in kilograms
13}
14
15interface BehavioralTraits {
16 poziomAgresji: number; // 1-10
17 terytorialny: boolean;
18 social: boolean;
19}
20
21// Creating a type that is the intersection of three interfaces
22type FullDinosaurProfile = BasicDinosaur & PhysicalTraits & BehavioralTraits;
23
24// Now the object must implement all properties from all three interfaces
25const velociraptor: FullDinosaurProfile = {
26 // From BasicDinosaur
27 id: 101,
28 nazwa: "Blue",
29 species: "Velociraptor",
30 wiek: 4,
31
32 // From PhysicalTraits
33 height: 0.5,
34 length: 2,
35 waga: 15,
36
37 // From BehavioralTraits
38 poziomAgresji: 8,
39 terytorialny: true,
40 social: true
41};Intersection types are especially useful when building complex systems in Jurassic Park:
1// Defining different abilities
2interface Swimming {
3 swimmingSpeed: number; // km/h
4 maxDepth: number; // meters
5 underwaterEndurance: number; // minutes
6}
7
8interface Flying {
9 wingspan: number; // meters
10 flightSpeed: number; // km/h
11 maxAltitude: number; // meters
12}
13
14// A dinosaur that can both swim and fly (like Pteranodon)
15type FlyingISwimmingDinosaur = BasicDinosaur & Flying & Swimming;
16
17const pteranodon: FlyingISwimmingDinosaur = {
18 id: 201,
19 name: "Pterri",
20 species: "Pteranodon",
21 age: 12,
22
23 // Flying abilities
24 wingspan: 8,
25 flightSpeed: 90,
26 maxAltitude: 500,
27
28 // Swimming abilities
29 swimmingSpeed: 20,
30 maxDepth: 10,
31 underwaterEndurance: 3
32};1// Basic security interface for all enclosures
2interface BasicSecuritySystem {
3 doubleFence: boolean;
4 videoMonitoring: boolean;
5 motionSensors: boolean;
6}
7
8// Additional security measures for predators
9interface PredatorSecurityMeasures {
10 electricVoltage: number; // volts
11 wallThickness: number; // cm
12 securityDome: boolean;
13}
14
15// Full security system for predator enclosure
16type PredatorEnclosure = BasicSecuritySystem & PredatorSecurityMeasures;
17
18// Using intersection to add enclosure information
19type TRexEnclosure = PredatorEnclosure & {
20 area: number; // hectares
21 terrainLayout: string[];
22 realisticVegetation: boolean;
23};
24
25const rexEnclosure: TRexEnclosure = {
26 // BasicSecuritySystem
27 doubleFence: true,
28 videoMonitoring: true,
29 motionSensors: true,
30
31 // PredatorSecurityMeasures
32 electricVoltage: 10000,
33 wallThickness: 50,
34 securityDome: false,
35
36 // Specific to TRexEnclosure
37 area: 8,
38 terrainLayout: ["tall grass", "hills", "stream"],
39 realisticVegetation: true
40};1// Observation system interface
2interface ObservationSystem {
3 cameras: number;
4 resolution: string;
5 infraredTracking: boolean;
6
7 startRecording(): void;
8 stopRecording(): void;
9}
10
11// Alarm system interface
12interface AlarmSystem {
13 soundSignal: boolean;
14 lightSignal: boolean;
15 smsNotification: boolean;
16
17 triggerAlarm(level: number): void;
18 disableAlarm(): void;
19}
20
21// Complex security system combining both functionalities
22type ParkSecuritySystem = ObservationSystem & AlarmSystem;
23
24// Implementation of the security system
25class FullSecuritySystem implements ParkSecuritySystem {
26 // ObservationSystem
27 cameras: number = 24;
28 resolution: string = "4K";
29 infraredTracking: boolean = true;
30
31 startRecording(): void {
32 console.log("Starting recording from all cameras");
33 }
34
35 stopRecording(): void {
36 console.log("Stopping recording");
37 }
38
39 // AlarmSystem
40 soundSignal: boolean = true;
41 lightSignal: boolean = true;
42 smsNotification: boolean = true;
43
44 triggerAlarm(level: number): void {
45 console.log(`Activating level ${level} alarm!`);
46 if (this.soundSignal) console.log("Sirens activated");
47 if (this.lightSignal) console.log("Warning lights activated");
48 if (this.smsNotification) console.log("Sending SMS notifications to staff");
49 }
50
51 disableAlarm(): void {
52 console.log("Disabling all alarm systems");
53 }
54
55 // Additional methods specific to the full system
56 initiateSecurityProcedure(threatCode: string): void {
57 console.log(`Initiating security procedure: ${threatCode}`);
58 this.startRecording();
59 this.triggerAlarm(threatCode === "RED" ? 3 : 1);
60 }
61}
62
63// Using the system
64const securitySystem = new FullSecuritySystem();
65securitySystem.initiateSecurityProcedure("RED");The true power of TypeScript is revealed when we combine union and intersection types into more complex structures - just as Dr. Wu combines DNA from different species to create entirely new dinosaurs.
1// Basic dinosaur types
2interface Dinosaur {
3 id: number;
4 name: string;
5 species: string;
6}
7
8interface LandDinosaur {
9 environment: "land";
10 runSpeed: number; // km/h
11}
12
13interface WaterDinosaur {
14 environment: "water";
15 swimmingSpeed: number; // km/h
16 maxDepth: number; // meters
17}
18
19interface AirDinosaur {
20 environment: "air";
21 flightSpeed: number; // km/h
22 maxAltitude: number; // meters
23}
24
25// Types for different abilities
26interface HuntingAbilities {
27 biteForce: number; // PSI
28 huntingStrategy: "ambush" | "chase" | "pack";
29}
30
31interface HerbivoreDefensiveAbilities {
32 armor: boolean;
33 horns: boolean;
34 spikes: boolean;
35 defensiveStrength: number; // 1-10
36}
37
38// Complex type using union and intersection
39type ParkDinosaur = Dinosaur &
40 (
41 (LandDinosaur & HuntingAbilities) |
42 (LandDinosaur & HerbivoreDefensiveAbilities) |
43 (WaterDinosaur & HuntingAbilities) |
44 (AirDinosaur & HuntingAbilities)
45 );
46
47// Examples of use
48const tyrannosaurus: ParkDinosaur = {
49 id: 1,
50 name: "Rex",
51 species: "Tyrannosaurus Rex",
52 environment: "land",
53 runSpeed: 30,
54 biteForce: 12800,
55 huntingStrategy: "chase"
56};
57
58const stegosaurus: ParkDinosaur = {
59 id: 2,
60 name: "Spike",
61 species: "Stegosaurus",
62 environment: "land",
63 runSpeed: 7,
64 armor: true,
65 horns: false,
66 spikes: true,
67 defensiveStrength: 8
68};
69
70// Function handling complex types
71function analyzeDinosaur(dino: ParkDinosaur): void {
72 console.log(`Analyzing: ${dino.name} (${dino.species})`);
73
74 // Check the environment
75 if (dino.environment === "land") {
76 console.log(`Running speed: ${dino.runSpeed} km/h`);
77
78 // Check whether it's a predator or herbivore
79 if ("biteForce" in dino) {
80 console.log(`Predator - bite force: ${dino.biteForce} PSI`);
81 console.log(`Hunting strategy: ${dino.huntingStrategy}`);
82 } else if ("defensiveStrength" in dino) {
83 console.log("Herbivore with defensive mechanisms:");
84 if (dino.armor) console.log("- Has armor");
85 if (dino.horns) console.log("- Has horns");
86 if (dino.spikes) console.log("- Has spikes");
87 console.log(`Defensive strength: ${dino.defensiveStrength}/10`);
88 }
89 } else if (dino.environment === "water") {
90 console.log(`Aquatic predator - swimming speed: ${dino.swimmingSpeed} km/h`);
91 console.log(`Maximum depth: ${dino.maxDepth} m`);
92 } else if (dino.environment === "air") {
93 console.log(`Aerial predator - flying speed: ${dino.flightSpeed} km/h`);
94 console.log(`Maximum flight altitude: ${dino.maxAltitude} m`);
95 }
96}
97
98analyzeDinosaur(tyrannosaurus);
99console.log("---");
100analyzeDinosaur(stegosaurus);Union and intersection types are like Dr. Wu's advanced genetic engineering techniques - they give us extraordinary power and flexibility in designing complex TypeScript systems.
| Type | Operator | Jurassic Park analogy | Use | |------|----------|----------------------|-----| | Union |
| | Enclosure that can hold different dinosaurs (but only one kind at a time) | Variables that can have different types |
| Discriminated Union | | with discriminating property | Dinosaur identification system for different species | Safe handling of different type variants |
| Intersection | & | Combining DNA from different dinosaurs | Combining interfaces/types into more complex structures |Dr. Wu summarizes today's lesson: "TypeScript, like our genetics laboratory, gives us enormous possibilities. Thanks to union and intersection types we can create systems precisely tailored to our needs, combining simple elements into complex structures. However remember - with great power comes great responsibility. Use this knowledge wisely to create a safe and reliable park."