JavaScript and TypeScript course Β· Module 6: TypeScript Basics

Complex types (array, tuple, enum)

8 min read
In this lesson7

Welcome back to Jurassic Park! After getting to know the basic data types in TypeScript, we're ready to move on to more advanced structures. In today's lesson Dr. Wu will take us to the laboratory, where we'll experiment with more complex data types.

Just as in Jurassic Park it's not enough to know individual species - you need to understand how entire ecosystems and dinosaur hierarchies work - so in TypeScript it's not enough to know only the basic types. We need to learn how to organize data into more complex structures.

Array types (Array)

Arrays in TypeScript are like enclosures in Jurassic Park - they serve to store collections of similar elements. We can define the type of an array in two ways:

1// Defining an array of dinosaurs
2let dinosaury: string[] = ["Tyrannosaurus", "Velociraptor", "Triceratops"];
3
4// Alternative notation
5let dinosauryAlt: Array<string> = ["Brachiosaurus", "Stegosaurus", "Ankylosaurus"];
6
7// Array of numbers - e.g. dinosaur weights in kilograms
8let wagi: number[] = [7500, 350, 8500, 35000, 5500];
9
10// Accessing array elements
11console.log(`First dinosaur: ${dinosaury[0]}`); // Tyrannosaurus
12console.log(`Number of dinosaurs: ${dinosaury.length}`); // 3
13
14// Adding to the array
15dinosaury.push("Spinosaurus");
16console.log(`Now we have ${dinosaury.length} dinosaurs`); // 4
17
18// Iterating over the array
19dinosaury.forEach(dino => {
20  console.log(`Studying behavior: ${dino}`);
21});

Arrays can also store more complex objects:

1// Defining an interface for a dinosaur object
2interface Dinosaur {
3  id: number;
4  name: string;
5  species: string;
6  isPredator: boolean;
7  ageMillionYears: number;
8}
9
10// Array of dinosaur objects
11const specimenCollection: Dinosaur[] = [
12  { id: 1, name: "Rex", species: "Tyrannosaurus", isPredator: true, ageMillionYears: 68 },
13  { id: 2, name: "Blue", species: "Velociraptor", isPredator: true, ageMillionYears: 75 },
14  { id: 3, name: "Trixie", species: "Triceratops", isPredator: false, ageMillionYears: 68 }
15];
16
17// Filtering the array - find all predators
18const predators = specimenCollection.filter(dino => dino.isPredator);
19console.log(`Number of predators: ${predators.length}`); // 2
20
21// Transforming the array - get only dinosaur names
22const dinosaurNames = specimenCollection.map(dino => dino.name);
23console.log(`Specimen names: ${dinosaurNames.join(", ")}`); // "Rex, Blue, Trixie"

Multidimensional arrays

Multidimensional arrays can be used to represent more complex data, such as a map of enclosures in Jurassic Park:

1// Two-dimensional array representing the Jurassic Park map
2// 0 = inaccessible terrain, 1 = path, 2 = herbivore enclosure, 3 = predator enclosure
3const mapaParkuJurajskiego: number[][] = [
4  [0, 0, 2, 2, 2, 0, 0],
5  [0, 1, 1, 1, 1, 1, 0],
6  [0, 1, 3, 3, 3, 1, 0],
7  [0, 1, 1, 1, 1, 1, 0],
8  [0, 0, 2, 2, 2, 0, 0]
9];
10
11// Checking what is at a given position
12function checkTerrain(x: number, y: number): string {
13  const terrain = mapaParkuJurajskiego[y][x];
14  switch (terrain) {
15    case 0: return "Inaccessible terrain";
16    case 1: return "Visitor path";
17    case 2: return "Herbivore enclosure";
18    case 3: return "WARNING! Predator enclosure!";
19    default: return "Unknown terrain";
20  }
21}
22
23console.log(checkTerrain(2, 2)); // "WARNING! Predator enclosure!"

Arrays with different element types - Tuple

Tuples are a special kind of array where we know the exact number of elements and their types. It's like transporting dinosaurs - the type of vehicle and the special safety measures appropriate for each species must be strictly defined.

1// Tuple representing employee data: [id, name, position, access level]
2type PracownikParku = [number, string, string, number];
3
4const pracownicy: PracownikParku[] = [
5  [1, "John Hammond", "Director", 5],
6  [2, "Dr. Alan Grant", "Paleontologist", 4],
7  [3, "Dr. Ellie Sattler", "Paleobotanist", 4],
8  [4, "Dennis Nedry", "Programmer", 3]
9];
10
11// Tuple with different element types
12// Format: [DNA code, species, age in millions of years, is predator]
13type KodGenetyczny = [string, string, number, boolean];
14
15const kodTRex: KodGenetyczny = ["ACGTTCAGC", "Tyrannosaurus Rex", 68, true];
16
17// Tuple destructuring
18const [dnaCode, species, age, predator] = kodTRex;
19console.log(`DNA: ${dnaCode}, Species: ${species}`);
20
21// Optional tuple elements (from TypeScript 3.0)
22// Format: [id, name, optional RFID tag]
23type OkazMuzealny = [number, string, string?];
24
25const okazDinosaura: OkazMuzealny = [42, "Velociraptor Skull"];
26const okazZTagiem: OkazMuzealny = [43, "Dinosaur Egg", "RFID-J-43"];

Enumeration types (Enum)

Enumeration types (Enum) in TypeScript are like safety categories in Jurassic Park - they help define a set of named constants that can be used in code.

1// Defining an enumeration type for threat categories
2enum ThreatCategory {
3  Low,        // 0
4  Moderate,   // 1
5  High,       // 2
6  Critical,   // 3
7  RedLevel    // 4
8}
9
10// Using the enum
11let tRexEnclosureThreat: ThreatCategory = ThreatCategory.High;
12
13if (tRexEnclosureThreat >= ThreatCategory.High) {
14  console.log("Double electric fencing recommended!");
15}
16
17// Accessing by value
18const categoryDescription = (level: ThreatCategory): string => {
19  switch (level) {
20    case ThreatCategory.Low: return "Standard precautions";
21    case ThreatCategory.Moderate: return "Increased vigilance";
22    case ThreatCategory.High: return "Double security";
23    case ThreatCategory.Critical: return "No visitor access";
24    case ThreatCategory.RedLevel: return "Park evacuation!";
25    default: return "Unknown threat level";
26  }
27};
28
29console.log(categoryDescription(tRexEnclosureThreat)); // "Double security"
30
31// Enum with specified values
32enum DinosaurStatus {
33  Inactive = 0,
34  LimitedActivity = 5,
35  Active = 10,
36  VeryActive = 15,
37  InterventionRequired = 20
38}
39
40let statusRexT: StatusDinosaura = StatusDinosaura.BardzoAktywny;
41console.log(`T-Rex activity level: ${statusRexT}`); // 15
42
43// String enum
44enum GatunekDinosaura {
45  TRex = "Tyrannosaurus Rex",
46  Raptor = "Velociraptor",
47  Tric = "Triceratops",
48  Stego = "Stegosaurus",
49  Brach = "Brachiosaurus"
50}
51
52let publicFavorite: GatunekDinosaura = GatunekDinosaura.TRex;
53console.log(`Today's attraction: ${publicFavorite}`); // "Tyrannosaurus Rex"

Const Enum

Const Enum is a TypeScript optimization that eliminates the enum object at compile time, replacing enum references directly with values.

1// Defining a Const Enum
2const enum SecurityZone {
3  Public = 1,
4  Staff = 2,
5  RestrictedAccess = 3,
6  Laboratories = 4,
7  Danger = 5
8}
9
10// Using the Const Enum
11function checkPermissions(accessLevel: number, requiredZone: SecurityZone): boolean {
12  return accessLevel >= requiredZone;
13}
14
15// Calling with enum values
16const drWuAccess = checkPermissions(5, SecurityZone.Laboratories); // true
17const staffAccess = checkPermissions(2, SecurityZone.Danger); // false

Practical application of complex types

Let's look at how we can use these complex types to build a Jurassic Park management system:

1// Defining interfaces and types
2enum HealthStatus {
3  Excellent = "excellent",
4  Good = "good",
5  NeedsAttention = "needs attention",
6  Sick = "sick",
7  Critical = "critical"
8}
9
10enum DietClassification {
11  Herbivore = "herbivore",
12  Omnivore = "omnivore",
13  Predator = "predator"
14}
15
16interface VitalSigns {
17  temperature: number;
18  heartRate: number;
19  bloodPressure: [number, number]; // Tuple [systolic, diastolic]
20  stressLevel: number; // 1-10
21}
22
23interface Dinosaur {
24  id: number;
25  name: string;
26  species: string;
27  dietClassification: DietClassification;
28  age: number;
29  weight: number;
30  status: HealthStatus;
31  vitalSigns: VitalSigns;
32  dailyActivityArray: number[]; // Activity level in 3-hour periods (8 values for full day)
33}
34
35// Creating a dinosaur management system
36class DinosaurManagementSystem {
37  private dinosaurs: Dinosaur[] = [];
38
39  addDinosaur(dino: Dinosaur): void {
40    this.dinosaurs.push(dino);
41    console.log(`Added dinosaur: ${dino.name} (${dino.species})`);
42  }
43
44  findDinosaur(id: number): Dinosaur | undefined {
45    return this.dinosaurs.find(d => d.id === id);
46  }
47
48  updateHealthStatus(id: number, newStatus: HealthStatus): boolean {
49    const dino = this.findDinosaur(id);
50    if (dino) {
51      dino.status = newStatus;
52      console.log(`Updated health status of ${dino.name} to: ${newStatus}`);
53      return true;
54    }
55    return false;
56  }
57
58  getPredators(): Dinosaur[] {
59    return this.dinosaurs.filter(d => d.dietClassification === DietClassification.Predator);
60  }
61
62  getDinosaursNeedingAttention(): Dinosaur[] {
63    return this.dinosaurs.filter(d =>
64      d.status === HealthStatus.NeedsAttention ||
65      d.status === HealthStatus.Sick ||
66      d.status === HealthStatus.Critical
67    );
68  }
69
70  calculateAverageActivity(id: number): number | null {
71    const dino = this.findDinosaur(id);
72    if (dino) {
73      const sum = dino.dailyActivityArray.reduce((acc, val) => acc + val, 0);
74      return sum / dino.dailyActivityArray.length;
75    }
76    return null;
77  }
78
79  prepareDailyReport(): [string, number, HealthStatus][] {
80    // Returns a tuple [name, id, status] for each dinosaur
81    return this.dinosaurs.map(d => [d.name, d.id, d.status]);
82  }
83}
84
85// Sample usage
86const systemJP = new DinosaurManagementSystem();
87
88// Adding dinosaurs to the system
89systemJP.addDinosaur({
90  id: 1,
91  name: "Rexy",
92  species: "Tyrannosaurus Rex",
93  dietClassification: DietClassification.Predator,
94  age: 7,
95  weight: 8000,
96  status: HealthStatus.Good,
97  vitalSigns: {
98    temperature: 38.2,
99    heartRate: 70,
100    bloodPressure: [140, 90],
101    stressLevel: 3
102  },
103  dailyActivityArray: [2, 1, 0, 0, 1, 3, 5, 2]
104});
105
106systemJP.addDinosaur({
107  id: 2,
108  name: "Blue",
109  species: "Velociraptor",
110  dietClassification: DietClassification.Predator,
111  age: 3,
112  weight: 350,
113  status: HealthStatus.Excellent,
114  vitalSigns: {
115    temperature: 39.1,
116    heartRate: 95,
117    bloodPressure: [130, 85],
118    stressLevel: 2
119  },
120  dailyActivityArray: [4, 2, 1, 0, 2, 5, 6, 3]
121});
122
123systemJP.addDinosaur({
124  id: 3,
125  name: "Spike",
126  species: "Stegosaurus",
127  dietClassification: DietClassification.Herbivore,
128  age: 12,
129  weight: 5000,
130  status: HealthStatus.NeedsAttention,
131  vitalSigns: {
132    temperature: 37.5,
133    heartRate: 50,
134    bloodPressure: [125, 80],
135    stressLevel: 6
136  },
137  dailyActivityArray: [3, 3, 2, 1, 3, 2, 2, 1]
138});
139
140// Using the system
141const predators = systemJP.getPredators();
142console.log(`Number of predators in the park: ${predators.length}`);
143
144const needingAttention = systemJP.getDinosaursNeedingAttention();
145console.log(`Number of dinosaurs needing attention: ${needingAttention.length}`);
146
147// Daily report example (array of tuples)
148const dailyReport = systemJP.prepareDailyReport();
149console.log("\nDaily dinosaur status report:");
150dailyReport.forEach(([name, id, status]) => {
151  console.log(`- ${name} (ID: ${id}): ${status}`);
152});
153
154// Status update example
155systemJP.updateHealthStatus(1, HealthStatus.NeedsAttention);

Summary of complex types

TypeUse in Jurassic ParkExample
ArrayCollections of similar elements, e.g. list of dinosaursstring[], Dinosaur[]
Multidimensional ArraySpatial representation, e.g. park mapnumber[][]
TupleFixed structures with defined types and sizes, e.g. genetic data[string, string, number, boolean]
EnumDefined categories, e.g. threat levelsenum ThreatCategory {...}
Const EnumOptimized enums for performanceconst enum SecurityZone {...}

Remember that choosing the right data types in a project is like designing safe and efficient enclosures in Jurassic Park - good design at the start saves many problems in the future!

Dr. Wu always says: "Precise control of types is the key to success - both in the genetics laboratory and in TypeScript code."

Code for this lesson: index.ts
1// Complex types in Jurassic Park
2console.log("TypeScript complex types\n");
3
4// ===========================================
5// 1. ARRAY - arrays
6// ===========================================
7console.log("=== ARRAYS ===");
8
9// Two ways to define arrays
10let dinosaurs: string[] = ["Tyrannosaurus", "Velociraptor", "Triceratops"];
11let weights: Array<number> = [7500, 350, 8500, 35000];
12
13console.log("Dinosaurs:", dinosaurs);
14console.log("First dinosaur:", dinosaurs[0]);
15console.log("Count:", dinosaurs.length);
16
17// Array operations
18dinosaurs.push("Spinosaurus");
19console.log("After push:", dinosaurs);
20
21// Iteration
22dinosaurs.forEach((dino, index) => {
23  console.log(`${index + 1}. ${dino}`);
24});
25
26// Array of objects
27interface Dinosaur {
28  id: number;
29  name: string;
30  species: string;
31  isCarnivore: boolean;
32  ageMillionYears: number;
33}
34
35const collection: Dinosaur[] = [
36  { id: 1, name: "Rex", species: "Tyrannosaurus", isCarnivore: true, ageMillionYears: 68 },
37  { id: 2, name: "Blue", species: "Velociraptor", isCarnivore: true, ageMillionYears: 75 },
38  { id: 3, name: "Trike", species: "Triceratops", isCarnivore: false, ageMillionYears: 68 }
39];
40
41console.log("\nDinosaur collection:", collection);
42
43// Filtering
44const carnivores = collection.filter(d => d.isCarnivore);
45console.log("Carnivores:", carnivores.map(d => d.name));
46
47// ===========================================
48// 2. TUPLE - tuples
49// ===========================================
50console.log("\n=== TUPLES ===");
51
52// Tuple - array with fixed length and types
53let dinoData: [string, number, boolean] = ["T-Rex", 8000, true];
54
55console.log("Species:", dinoData[0]);
56console.log("Weight:", dinoData[1]);
57console.log("Is dangerous:", dinoData[2]);
58
59// Tuple with named elements (TS 4.0+)
60type DinoTuple = [species: string, weight: number, dangerous: boolean];
61let velociraptor: DinoTuple = ["Velociraptor", 350, true];
62
63console.log("Velociraptor data:", velociraptor);
64
65// Tuple with optional elements
66type PaddockData = [id: string, capacity: number, isLocked?: boolean];
67let paddock1: PaddockData = ["P-01", 5, true];
68let paddock2: PaddockData = ["P-02", 10]; // isLocked is optional
69
70console.log("Paddock 1:", paddock1);
71console.log("Paddock 2:", paddock2);
72
73// ===========================================
74// 3. ENUM - enumerations
75// ===========================================
76console.log("\n=== ENUM - ENUMERATIONS ===");
77
78// Numeric enum
79enum SecurityLevel {
80  Green = 1,
81  Yellow = 2,
82  Orange = 3,
83  Red = 4,
84  Critical = 5
85}
86
87let currentSecurity: SecurityLevel = SecurityLevel.Orange;
88console.log("Security level:", SecurityLevel[currentSecurity]); // "Orange"
89console.log("Security value:", currentSecurity); // 3
90
91// String enum
92enum DinosaurDiet {
93  Carnivore = "CARNIVORE",
94  Herbivore = "HERBIVORE",
95  Omnivore = "OMNIVORE"
96}
97
98let trexDiet: DinosaurDiet = DinosaurDiet.Carnivore;
99console.log("T-Rex diet:", trexDiet); // "CARNIVORE"
100
101// Enum with calculations
102enum PaddockSize {
103  Small = 100,
104  Medium = 500,
105  Large = 1000,
106  ExtraLarge = Large * 2 // 2000
107}
108
109console.log("Extra large paddock:", PaddockSize.ExtraLarge);
110
111// Const enum (optimization)
112const enum DinosaurPeriod {
113  Triassic = "TRIASSIC",
114  Jurassic = "JURASSIC",
115  Cretaceous = "CRETACEOUS"
116}
117
118let trexPeriod: DinosaurPeriod = DinosaurPeriod.Cretaceous;
119console.log("T-Rex period:", trexPeriod);
120
121// ===========================================
122// 4. Helper functions for complex types
123// ===========================================
124console.log("\n=== OPERATIONS ON COMPLEX TYPES ===");
125
126// A function for arrays
127function getTotalWeight(dinosaurs: Dinosaur[]): number {
128  return weights.reduce((sum, w) => sum + w, 0);
129}
130
131console.log("Total weight:", getTotalWeight(collection));
132
133// A function for tuples
134function createDinoTuple(species: string, weight: number, dangerous: boolean): DinoTuple {
135  return [species, weight, dangerous];
136}
137
138const newDino = createDinoTuple("Stegosaurus", 5000, false);
139console.log("New dinosaur:", newDino);
140
141// A function for enums
142function getSecurityColor(level: SecurityLevel): string {
143  switch (level) {
144    case SecurityLevel.Green:
145      return "Green";
146    case SecurityLevel.Yellow:
147      return "Yellow";
148    case SecurityLevel.Orange:
149      return "Orange";
150    case SecurityLevel.Red:
151      return "Red";
152    case SecurityLevel.Critical:
153      return "Critical";
154    default:
155      return "Unknown";
156  }
157}
158
159console.log("Current security:", getSecurityColor(currentSecurity));
160
161// ===========================================
162// 5. Nested structures
163// ===========================================
164console.log("\n=== NESTED STRUCTURES ===");
165
166interface Paddock {
167  id: string;
168  size: PaddockSize;
169  security: SecurityLevel;
170  dinosaurs: Dinosaur[];
171  coordinates: [latitude: number, longitude: number];
172}
173
174const trexPaddock: Paddock = {
175  id: "P-01",
176  size: PaddockSize.Large,
177  security: SecurityLevel.Critical,
178  dinosaurs: [collection[0]],
179  coordinates: [22.3964, -159.5261]
180};
181
182console.log("T-Rex Paddock:", trexPaddock);
183console.log("Coordinates:", trexPaddock.coordinates);
184console.log("Dinosaurs in paddock:", trexPaddock.dinosaurs.length);
185
186// ===========================================
187// 6. Summary
188// ===========================================
189console.log("\n=== SUMMARY ===");
190console.log("Array - collections of elements of the same type");
191console.log("Tuple - arrays of fixed length and different types");
192console.log("Enum - named constant values");
193console.log("Nesting - combining complex structures");
194
195console.log("\nComplex types 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. 1. What is a tuple in TypeScript?

Hands-on tasks in the game

  • Code editor

    Define a species array (string[]) and a tuple [string, number] for the dinosaur name and weight.

  • Vertical ordering

    Order TypeScript types from most general to most specific:

  • Horizontal ordering

    Arrange the elements of a tuple declaration in TypeScript:

Useful articles