Cours JavaScript et TypeScript · Module 6: Bases de TypeScript

Les types complexes (array, tuple, enum)

9 min de lecture
Dans cette leçon7

Bienvenue de nouveau au Jurassic Park ! Après avoir fait connaissance avec les types de données de base en TypeScript, nous sommes prêts à passer à des structures plus avancées. Dans la leçon d'aujourd'hui, le Dr Wu nous emmènera au laboratoire, où nous expérimenterons des types de données plus complexes.

Tout comme au Jurassic Park il ne suffit pas de connaître les espèces individuelles - il faut comprendre le fonctionnement d'écosystèmes entiers et des hiérarchies de dinosaures -, en TypeScript il ne suffit pas de connaître uniquement les types de base. Nous devons apprendre à organiser les données en structures plus complexes.

Les types tableau (Array)

Les tableaux en TypeScript sont comme les enclos du Jurassic Park : ils servent à stocker des collections d'éléments similaires. Nous pouvons définir le type d'un tableau de deux façons :

1// Définition d'un tableau de dinosaures
2let dinosaurs: string[] = ["Tyrannosaurus", "Velociraptor", "Triceratops"];
3
4// Notation alternative
5let dinosaursAlt: Array<string> = ["Brachiosaurus", "Stegosaurus", "Ankylosaurus"];
6
7// Tableau de nombres - ex. poids des dinosaures en kilogrammes
8let weights: number[] = [7500, 350, 8500, 35000, 5500];
9
10// Accès aux éléments du tableau
11console.log(`First dinosaur: ${dinosaurs[0]}`); // Tyrannosaurus
12console.log(`Number of dinosaurs: ${dinosaurs.length}`); // 3
13
14// Ajout au tableau
15dinosaurs.push("Spinosaurus");
16console.log(`Now we have ${dinosaurs.length} dinosaurs`); // 4
17
18// Itération sur le tableau
19dinosaurs.forEach(dino => {
20  console.log(`Studying behavior: ${dino}`);
21});

Les tableaux peuvent aussi stocker des objets plus complexes :

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// Filtrage du tableau - trouver tous les prédateurs
18const predators = specimenCollection.filter(dino => dino.isPredator);
19console.log(`Number of predators: ${predators.length}`); // 2
20
21// Transformation du tableau - récupérer uniquement les noms des dinosaures
22const dinosaurNames = specimenCollection.map(dino => dino.name);
23console.log(`Specimen names: ${dinosaurNames.join(", ")}`); // "Rex, Blue, Trixie"

Les tableaux multidimensionnels

Les tableaux multidimensionnels peuvent servir à représenter des données plus complexes, comme une carte des enclos du Jurassic Park :

1// Tableau à deux dimensions représentant la carte du Jurassic Park
2// 0 = terrain inaccessible, 1 = chemin, 2 = enclos d'herbivores, 3 = enclos de prédateurs
3const jurassicParkMap: 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// Vérifier ce qui se trouve à une position donnée
12function checkTerrain(x: number, y: number): string {
13  const terrain = jurassicParkMap[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!"

Les tableaux avec des éléments de types différents - le Tuple

Les tuples sont un type particulier de tableau où nous connaissons le nombre exact d'éléments et leurs types. C'est comme le transport des dinosaures : le type de véhicule et les mesures de sécurité particulières adaptées à chaque espèce doivent être strictement définis.

1// Tuple représentant les données d'un employé : [id, nom, poste, niveau d'accès]
2type ParkEmployee = [number, string, string, number];
3
4const employees: ParkEmployee[] = [
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 avec des éléments de types différents
12// Format : [code ADN, espèce, âge en millions d'années, est un prédateur]
13type GeneticCode = [string, string, number, boolean];
14
15const trexCode: GeneticCode = ["ACGTTCAGC", "Tyrannosaurus Rex", 68, true];
16
17// Déstructuration du tuple
18const [dnaCode, species, age, predator] = trexCode;
19console.log(`DNA: ${dnaCode}, Species: ${species}`);
20
21// Éléments de tuple optionnels (depuis TypeScript 3.0)
22// Format : [id, nom, puce RFID optionnelle]
23type MuseumSpecimen = [number, string, string?];
24
25const dinosaurSpecimen: MuseumSpecimen = [42, "Velociraptor Skull"];
26const specimenWithTag: MuseumSpecimen = [43, "Dinosaur Egg", "RFID-J-43"];

Les types énumération (Enum)

Les types énumération (Enum) en TypeScript sont comme les catégories de sécurité du Jurassic Park : ils aident à définir un ensemble de constantes nommées qui peuvent être utilisées dans le code.

1// Définition d'un type énumération pour les catégories de menace
2enum ThreatCategory {
3  Low,        // 0
4  Moderate,   // 1
5  High,       // 2
6  Critical,   // 3
7  RedLevel    // 4
8}
9
10// Utilisation de l'enum
11let tRexEnclosureThreat: ThreatCategory = ThreatCategory.High;
12
13if (tRexEnclosureThreat >= ThreatCategory.High) {
14  console.log("Double electric fencing recommended!");
15}
16
17// Accès par valeur
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 avec des valeurs spécifiées
32enum DinosaurStatus {
33  Inactive = 0,
34  LimitedActivity = 5,
35  Active = 10,
36  VeryActive = 15,
37  InterventionRequired = 20
38}
39
40let statusRexT: DinosaurStatus = DinosaurStatus.VeryActive;
41console.log(`T-Rex activity level: ${statusRexT}`); // 15
42
43// Enum de chaînes de caractères
44enum DinosaurSpecies {
45  TRex = "Tyrannosaurus Rex",
46  Raptor = "Velociraptor",
47  Tric = "Triceratops",
48  Stego = "Stegosaurus",
49  Brach = "Brachiosaurus"
50}
51
52let publicFavorite: DinosaurSpecies = DinosaurSpecies.TRex;
53console.log(`Today's attraction: ${publicFavorite}`); // "Tyrannosaurus Rex"

Const Enum

Le Const Enum est une optimisation de TypeScript qui élimine l'objet enum au moment de la compilation, en remplaçant directement les références à l'enum par leurs valeurs.

1// Définition d'un Const Enum
2const enum SecurityZone {
3  Public = 1,
4  Staff = 2,
5  RestrictedAccess = 3,
6  Laboratories = 4,
7  Danger = 5
8}
9
10// Utilisation du Const Enum
11function checkPermissions(accessLevel: number, requiredZone: SecurityZone): boolean {
12  return accessLevel >= requiredZone;
13}
14
15// Appel avec des valeurs d'enum
16const drWuAccess = checkPermissions(5, SecurityZone.Laboratories); // true
17const staffAccess = checkPermissions(2, SecurityZone.Danger); // false

Application pratique des types complexes

Voyons comment nous pouvons utiliser ces types complexes pour construire un système de gestion du Jurassic Park :

1// Définition des interfaces et des 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 [systolique, diastolique]
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[]; // Niveau d'activité par tranches de 3 heures (8 valeurs pour une journée complète)
33}
34
35// Création d'un système de gestion des dinosaures
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    // Renvoie un tuple [nom, id, statut] pour chaque dinosaure
81    return this.dinosaurs.map(d => [d.name, d.id, d.status]);
82  }
83}
84
85// Exemple d'utilisation
86const systemJP = new DinosaurManagementSystem();
87
88// Ajout de dinosaures au système
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// Utilisation du système
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// Exemple de rapport quotidien (tableau de 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// Exemple de mise à jour du statut
155systemJP.updateHealthStatus(1, HealthStatus.NeedsAttention);

Résumé des types complexes

TypeUtilisation au Jurassic ParkExemple
ArrayCollections d'éléments similaires, ex. liste de dinosauresstring[], Dinosaur[]
Array multidimensionnelReprésentation spatiale, ex. carte du parcnumber[][]
TupleStructures fixes avec types et tailles définis, ex. données génétiques[string, string, number, boolean]
EnumCatégories définies, ex. niveaux de menaceenum ThreatCategory {...}
Const EnumEnums optimisés pour la performanceconst enum SecurityZone {...}

N'oublie pas que choisir les bons types de données dans un projet, c'est comme concevoir des enclos sûrs et efficaces au Jurassic Park : une bonne conception dès le départ évite bien des problèmes à l'avenir !

Le Dr Wu le dit toujours : « Le contrôle précis des types est la clé du succès, aussi bien au laboratoire de génétique que dans le code TypeScript. »

Code de cette leçon : index.ts
1// Complex types in Jurassic Park
2console.log("TypeScript complex types\n");
3
4// ===========================================
5// 1. ARRAY - tableaux
6// ===========================================
7console.log("=== TABLEAUX (ARRAY) ===");
8
9// Deux façons de définir des tableaux
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// Opérations sur les tableaux
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=== TUPLE ===");
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 avec des elements nommés (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 avec des elements optionnels
66type PaddockData = [id: string, capacity: number, isLocked?: boolean];
67let paddock1: PaddockData = ["P-01", 5, true];
68let paddock2: PaddockData = ["P-02", 10]; // isLocked optionnel
69
70console.log("Paddock 1:", paddock1);
71console.log("Paddock 2:", paddock2);
72
73// ===========================================
74// 3. ENUM - énumérations
75// ===========================================
76console.log("\n=== ENUM - ÉNUMÉRATIONS ===");
77
78// Enum numérique
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// Enum textuel
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 avec des calculs
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 (optimisation)
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// Function pour les tableaux
127function getTotalWeight(dinosaurs: Dinosaur[]): number {
128  return weights.reduce((sum, w) => sum + w, 0);
129}
130
131console.log("Total weight:", getTotalWeight(collection));
132
133// Function pour les tuple
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// Function pour les enum
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. Résumé
188// ===========================================
189console.log("\n=== RÉSUMÉ ===");
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!");

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Qu’est-ce qu’un tuple en TypeScript ?

Tâches pratiques dans le jeu

  • Éditeur de code

    Définis un tableau species (string[]) et un tuple [string, number] pour le nom et le poids du dinosaure.

  • Tri vertical

    Ordonne les types TypeScript du plus général au plus spécifique :

  • Tri horizontal

    Ordonne les éléments d’une déclaration de tuple en TypeScript :

Articles utiles