Cours JavaScript et TypeScript · Module 6: Bases de TypeScript
Définitions d'interfaces
Dans cette leçon11
Bienvenue de nouveau au Jurassic Park ! Aujourd'hui, sous l'œil attentif du Dr Henry Wu, nous allons explorer un concept fondamental de TypeScript, tout aussi crucial pour l'organisation du code que les protocoles de sécurité le sont pour le bon fonctionnement du parc : les interfaces.
Que sont les interfaces en TypeScript ?
Les interfaces en TypeScript définissent des contrats dans le code et décrivent la structure des objets. Elles sont comme des protocoles détaillés qui précisent ce qu'un objet donné doit contenir. Dans le contexte du Jurassic Park, on peut comparer les interfaces aux normes de sécurité et aux protocoles de manipulation des différentes espèces de dinosaures : chaque espèce a ses propres exigences et traits spécifiques.
Syntaxe de base d'une interface
On définit une interface à l'aide du mot-clé interface :
1interface Dinosaur {
2 id: number;
3 name: string;
4 species: string;
5 age: number;
6 weight: number;
7}
8
9// On crée un objet qui doit satisfaire les exigences de l'interface
10const rex: Dinosaur = {
11 id: 1,
12 name: "Rex",
13 species: "Tyrannosaurus",
14 age: 7,
15 weight: 8000
16};
17
18// Le code suivant provoquerait une erreur car des propriétés obligatoires sont manquantes
19// const raptor: Dinosaur = {
20// id: 2,
21// name: "Blue"
22// };Les propriétés optionnelles
Certaines propriétés d'une interface peuvent être optionnelles, signalées par l'ajout d'un point d'interrogation (?). C'est comme marquer des procédures de manipulation optionnelles pour des dinosaures particuliers.
1interface DinosaurSpecimen {
2 id: number;
3 name: string;
4 species: string;
5 rfidTag?: string; // Puce RFID optionnelle
6 lastExamination?: Date; // Date du dernier examen optionnelle
7 notes?: string; // Notes de recherche optionnelles
8}
9
10// Objet satisfaisant l'interface, mais sans toutes les propriétés optionnelles
11const specimen1: DinosaurSpecimen = {
12 id: 101,
13 name: "Specimen A",
14 species: "Velociraptor"
15};
16
17// Objet avec certaines propriétés optionnelles
18const specimen2: DinosaurSpecimen = {
19 id: 102,
20 name: "Specimen B",
21 species: "Dilophosaurus",
22 rfidTag: "DILO-102",
23 notes: "Shows aggressive behavior in the presence of new objects"
24};Lecture seule (readonly)
Certaines propriétés ne devraient être modifiées qu'au moment de la création de l'objet. En TypeScript, on peut y parvenir grâce au modificateur readonly. C'est comme des procédures de sécurité qui, une fois établies, ne peuvent pas être modifiées pendant les opérations normales.
1interface GeneticCode {
2 readonly id: string;
3 readonly sequence: string;
4 readonly donorSpecies: string;
5 modifications: string[];
6 isolationDate: Date;
7}
8
9// Création du code génétique
10const trexCode: GeneticCode = {
11 id: "GEN-TREX-001",
12 sequence: "ACGTTGCAAGCTA...",
13 donorSpecies: "West African frog",
14 modifications: ["Gap DNA filling", "Aggression gene correction"],
15 isolationDate: new Date("2022-05-15")
16};
17
18// On peut ajouter de nouvelles modifications
19trexCode.modifications.push("Immune resistance enhancement");
20
21// Mais on ne peut pas modifier les valeurs des champs readonly
22// trexCode.id = "GEN-TREX-002"; // Erreur : Cannot assign to 'id' because it is a read-only property
23// trexCode.sequence = "GCTA..."; // Erreur : Cannot assign to 'sequence' because it is a read-only propertyInterfaces de fonction
Les interfaces peuvent aussi définir des types de fonction, ce qui est utile pour décrire des callbacks ou des fonctions passées en paramètre. Dans le contexte du parc, c'est comme définir des procédures standard pour gérer différentes situations.
1// Interface de la fonction de contrôle de sécurité
2interface SecurityCheck {
3 (threatLevel: number, parkZone: string): boolean;
4}
5
6// Implémentation d'une fonction conforme à l'interface
7const checkAccess: SecurityCheck = function(level, zone) {
8 // Logique de vérification d'accès
9 if (zone === "T-Rex Enclosure" && level < 5) {
10 return false; // Refuser l'accès à la zone du T-Rex au personnel en dessous du niveau 5
11 }
12
13 if (zone.includes("Laboratory") && level < 4) {
14 return false; // Refuser l'accès aux laboratoires au personnel en dessous du niveau 4
15 }
16
17 return true; // Accès autorisé dans les autres cas
18};
19
20// Utilisation de la fonction
21console.log(checkAccess(3, "T-Rex Enclosure")); // false
22console.log(checkAccess(5, "T-Rex Enclosure")); // true
23console.log(checkAccess(3, "Public area")); // trueLes types indexables
Les interfaces peuvent définir des types indexables, utiles quand nous avons des objets avec un nombre dynamique de propriétés du même type. Dans le contexte du parc, cela pourrait être une base de données de dinosaures indexée par leurs identifiants.
1// Interface pour une base de données de dinosaures
2interface DinosaursDatabase {
3 [id: string]: {
4 name: string;
5 species: string;
6 age: number;
7 health: "good" | "needs attention" | "critical";
8 };
9}
10
11// Création de la base de données
12const database: DinosaursDatabase = {
13 "TREX-001": {
14 name: "Rex",
15 species: "Tyrannosaurus",
16 age: 7,
17 health: "good"
18 },
19 "VELOC-001": {
20 name: "Blue",
21 species: "Velociraptor",
22 age: 4,
23 health: "good"
24 },
25 "TRIC-003": {
26 name: "Trike",
27 species: "Triceratops",
28 age: 10,
29 health: "needs attention"
30 }
31};
32
33// Accès aux données
34console.log(database["VELOC-001"].name); // "Blue"
35
36// Ajout d'une nouvelle entrée
37database["STEGO-002"] = {
38 name: "Spike",
39 species: "Stegosaurus",
40 age: 6,
41 health: "good"
42};Interfaces de classe
Les interfaces peuvent aussi définir des contrats que les classes doivent implémenter. C'est comme des normes opérationnelles que chaque service du parc doit respecter.
1// Interface pour les systèmes de sécurité
2interface SecuritySystem {
3 name: string;
4 isActive: boolean;
5 securityLevel: number;
6
7 activate(): void;
8 deactivate(): void;
9 checkStatus(): string;
10}
11
12// Classe implémentant l'interface
13class ElectronicFenceSystem implements SecuritySystem {
14 name: string;
15 isActive: boolean;
16 securityLevel: number;
17 voltage: number; // Propriété supplémentaire propre à cette classe
18
19 constructor(name: string, securityLevel: number, voltage: number) {
20 this.name = name;
21 this.isActive = false; // Inactif au départ
22 this.securityLevel = securityLevel;
23 this.voltage = voltage;
24 }
25
26 activate(): void {
27 this.isActive = true;
28 console.log(`System ${this.name} activated. Voltage: ${this.voltage}V`);
29 }
30
31 deactivate(): void {
32 this.isActive = false;
33 console.log(`System ${this.name} deactivated.`);
34 }
35
36 checkStatus(): string {
37 return this.isActive
38 ? `${this.name}: ACTIVE (Level ${this.securityLevel})`
39 : `${this.name}: INACTIVE`;
40 }
41
42 // Méthode propre à cette classe
43 increaseVoltage(by: number): void {
44 this.voltage += by;
45 console.log(`Voltage increased to ${this.voltage}V`);
46 }
47}
48
49// Utilisation de la classe
50const trexFence = new ElectronicFenceSystem("T-Rex Fence", 5, 10000);
51trexFence.activate();
52console.log(trexFence.checkStatus());
53trexFence.increaseVoltage(2000);Étendre des interfaces
Les interfaces peuvent étendre d'autres interfaces, un peu comme en biologie les espèces partagent des traits communs mais ont aussi leurs propres propriétés uniques.
1// Interface de base pour tous les animaux du parc
2interface Animal {
3 id: number;
4 species: string;
5 age: number;
6 weight: number;
7}
8
9// Interface pour les dinosaures étendant l'interface Animal
10interface Dinosaur extends Animal {
11 era: string; // ex. "Cretaceous", "Jurassic"
12 isPredator: boolean;
13 mainDiet: string[];
14}
15
16// Interface pour les dinosaures prédateurs étendant l'interface Dinosaur
17interface PredatoryDinosaur extends Dinosaur {
18 biteForce: number; // en PSI
19 runSpeed: number; // en km/h
20 huntingBehavior: string[];
21}
22
23// Création d'un objet conforme à l'interface PredatoryDinosaur
24const velociraptor: PredatoryDinosaur = {
25 id: 101,
26 species: "Velociraptor",
27 age: 4,
28 weight: 70,
29 era: "Cretaceous",
30 isPredator: true,
31 mainDiet: ["small mammals", "lizards", "other small dinosaurs"],
32 biteForce: 1000,
33 runSpeed: 60,
34 huntingBehavior: ["pack hunting", "ambush", "chase"]
35};La fusion de déclarations pour les interfaces
En TypeScript, il est possible de définir la même interface plusieurs fois : les déclarations seront fusionnées. C'est utile quand nous voulons ajouter de nouvelles propriétés à une interface existante.
1// Définition initiale du protocole de sécurité
2interface SecurityProtocol {
3 name: string;
4 threatLevel: number;
5 evacuationProcedure: string;
6}
7
8// Extension du protocole avec des propriétés supplémentaires
9interface SecurityProtocol {
10 responsibleStaff: string[];
11 responseTime: number; // en minutes
12}
13
14// L'objet doit contenir toutes les propriétés des deux déclarations
15const alphaProtocol: SecurityProtocol = {
16 name: "Protocol Alpha",
17 threatLevel: 3,
18 evacuationProcedure: "Evacuation towards the main control center",
19 responsibleStaff: ["Dr. Wu", "Owen Grady", "Claire Dearing"],
20 responseTime: 15
21};Interfaces et types
TypeScript propose aussi le mot-clé type pour définir des types. Bien que dans de nombreux cas les interfaces et les types soient interchangeables, il existe des différences entre eux :
1// Définition à l'aide d'une interface
2interface EvacuationPlan {
3 route: string[];
4 estimatedTime: number;
5 assemblyPoints: string[];
6}
7
8// Définition équivalente à l'aide d'un type
9type EvacuationPlanType = {
10 route: string[];
11 estimatedTime: number;
12 assemblyPoints: string[];
13};
14
15// Les deux fonctionnent de manière similaire pour un usage de base
16const planA: EvacuationPlan = {
17 route: ["Operations center", "Main Street", "Evacuation hall"],
18 estimatedTime: 25,
19 assemblyPoints: ["Operations center", "Evacuation hall"]
20};
21
22const planB: EvacuationPlanType = {
23 route: ["Gallimimus Enclosure", "Innovation Center", "Evacuation hall"],
24 estimatedTime: 30,
25 assemblyPoints: ["Innovation Center", "Evacuation hall"]
26};Principales différences entre interface et type :
Extensibilité :
- Les interfaces peuvent être étendues par plusieurs déclarations portant le même nom
- Les types ne peuvent pas être « rouverts » : une fois déclaré, un type ne peut plus être étendu
Types complexes :
- Les types peuvent représenter directement des types primitifs, des unions, des intersections
- Les interfaces sont davantage axées sur la définition de la forme des objets
1// Possible uniquement avec les types
2type DinosaurID = string | number;
3type EnclosureCoordinates = [number, number, string]; // Tuple
4type DinosaurStatus = "active" | "resting" | "sleeping"; // Union de littéraux
5
6// Plus naturel avec les interfaces
7interface Procedure {
8 name: string;
9 steps: string[];
10 executionTime: number;
11 responsibleStaff: string[];
12}Application pratique des interfaces
Voyons comment nous pouvons utiliser les interfaces pour modéliser le système de gestion du Jurassic Park :
1// Interfaces de base
2interface ParkPerson {
3 id: string;
4 firstName: string;
5 lastName: string;
6 accessLevel: 1 | 2 | 3 | 4 | 5;
7 hireDate: Date;
8}
9
10interface Location {
11 id: string;
12 name: string;
13 type: "enclosure" | "laboratory" | "operations center" | "public zone";
14 coordinates: [number, number]; // [latitude, longitude]
15 requiredAccessLevel: 1 | 2 | 3 | 4 | 5;
16}
17
18interface DinosaurSpecies {
19 name: string;
20 geologicalEra: string;
21 type: "herbivore" | "predator" | "omnivore";
22 averageWeight: number;
23 averageAge: number;
24 dangerLevel: 1 | 2 | 3 | 4 | 5;
25 dietRequirements: string[];
26 habitatRequirements: string[];
27}
28
29interface DinosaurSpecimen {
30 id: string;
31 species: string;
32 firstName?: string;
33 age: number;
34 weight: number;
35 gender: "male" | "female";
36 location: string; // ID de la localisation
37 healthStatus: "excellent" | "good" | "needs attention" | "sick" | "critical";
38 lastExamination: Date;
39 rfidTag: string;
40 notes?: string;
41}
42
43// Interfaces pour les systèmes de gestion
44interface SpecimenTrackingSystem {
45 getPosition(specimenId: string): [number, number];
46 trackMovement(specimenId: string, trackingTime: number): [number, number][];
47 checkIfInZone(specimenId: string, zoneId: string): boolean;
48 alertWhenOutsideZone(specimenId: string, callback: (specimen: DinosaurSpecimen) => void): void;
49}
50
51interface EmployeeManagementSystem {
52 addEmployee(employee: ParkPerson): void;
53 removeEmployee(id: string): boolean;
54 grantAccess(employeeId: string, level: 1 | 2 | 3 | 4 | 5): boolean;
55 checkPresenceInZone(employeeId: string, zoneId: string): boolean;
56}
57
58// Exemple d'implémentation
59class ParkManagementSystem {
60 private employees: ParkPerson[] = [];
61 private locations: Location[] = [];
62 private species: DinosaurSpecies[] = [];
63 private specimens: DinosaurSpecimen[] = [];
64
65 // Méthodes pour le personnel
66 addEmployee(employee: ParkPerson): void {
67 this.employees.push(employee);
68 console.log(`Added employee: ${employee.firstName} ${employee.lastName}`);
69 }
70
71 // Méthodes pour les localisations
72 addLocation(location: Location): void {
73 this.locations.push(location);
74 console.log(`Added location: ${location.name}`);
75 }
76
77 // Méthodes pour les espèces
78 addSpecies(species: DinosaurSpecies): void {
79 this.species.push(species);
80 console.log(`Added species: ${species.name}`);
81 }
82
83 // Méthodes pour les spécimens
84 addSpecimen(specimen: DinosaurSpecimen): void {
85 this.specimens.push(specimen);
86 console.log(`Added specimen: ${specimen.id} (${specimen.species})`);
87 }
88
89 // Méthodes de gestion
90 checkAccess(employeeId: string, locationId: string): boolean {
91 const employee = this.employees.find(p => p.id === employeeId);
92 const location = this.locations.find(l => l.id === locationId);
93
94 if (!employee || !location) return false;
95
96 return employee.accessLevel >= location.requiredAccessLevel;
97 }
98
99 findSpecimensBySpecies(speciesName: string): DinosaurSpecimen[] {
100 return this.specimens.filter(o => o.species === speciesName);
101 }
102
103 findSpecimensInZone(zoneId: string): DinosaurSpecimen[] {
104 return this.specimens.filter(o => o.location === zoneId);
105 }
106
107 generateParkStatusReport(): {
108 employeeCount: number;
109 locationCount: number;
110 speciesCount: number;
111 specimenCount: number;
112 specimenHealthStatus: {
113 excellent: number;
114 good: number;
115 needsAttention: number;
116 sick: number;
117 critical: number;
118 }
119 } {
120 // Analyse de l'état de santé de tous les spécimens
121 const healthStatus = {
122 excellent: 0,
123 good: 0,
124 needsAttention: 0,
125 sick: 0,
126 critical: 0
127 };
128
129 this.specimens.forEach(specimen => {
130 if (specimen.healthStatus === "excellent") healthStatus.excellent++;
131 else if (specimen.healthStatus === "good") healthStatus.good++;
132 else if (specimen.healthStatus === "needs attention") healthStatus.needsAttention++;
133 else if (specimen.healthStatus === "sick") healthStatus.sick++;
134 else if (specimen.healthStatus === "critical") healthStatus.critical++;
135 });
136
137 return {
138 employeeCount: this.employees.length,
139 locationCount: this.locations.length,
140 speciesCount: this.species.length,
141 specimenCount: this.specimens.length,
142 specimenHealthStatus: healthStatus
143 };
144 }
145}
146
147// Exemple d'utilisation
148const jurassicPark = new ParkManagementSystem();
149
150// Ajout d'une localisation
151jurassicPark.addLocation({
152 id: "LOC-001",
153 name: "T-Rex Enclosure",
154 type: "enclosure",
155 coordinates: [23.45, -67.89],
156 requiredAccessLevel: 4
157});
158
159// Ajout d'une espèce
160jurassicPark.addSpecies({
161 name: "Tyrannosaurus Rex",
162 geologicalEra: "Cretaceous",
163 type: "predator",
164 averageWeight: 7000,
165 averageAge: 30,
166 dangerLevel: 5,
167 dietRequirements: ["large amounts of meat", "calcium supplementation"],
168 habitatRequirements: ["large area", "natural vegetation", "water access"]
169});
170
171// Ajout d'un employé
172jurassicPark.addEmployee({
173 id: "EMP-001",
174 firstName: "Owen",
175 lastName: "Grady",
176 accessLevel: 5,
177 hireDate: new Date("2010-06-15")
178});
179
180// Ajout d'un spécimen
181jurassicPark.addSpecimen({
182 id: "DINO-001",
183 species: "Tyrannosaurus Rex",
184 firstName: "Rexy",
185 age: 7,
186 weight: 7500,
187 gender: "female",
188 location: "LOC-001",
189 healthStatus: "good",
190 lastExamination: new Date("2023-03-20"),
191 rfidTag: "RFID-TREX-001"
192});
193
194// Génération d'un rapport
195const report = jurassicPark.generateParkStatusReport();
196console.log("Jurassic Park Status Report:");
197console.log(`Number of employees: ${report.employeeCount}`);
198console.log(`Number of locations: ${report.locationCount}`);
199console.log(`Number of species: ${report.speciesCount}`);
200console.log(`Number of specimens: ${report.specimenCount}`);
201console.log("Specimen health status:", report.specimenHealthStatus);Résumé
Les interfaces en TypeScript sont un outil puissant pour modéliser les structures de données et définir des contrats dans le code. Dans le contexte de la gestion du Jurassic Park, elles nous permettent de définir précisément à quoi doivent ressembler les différents systèmes, procédures et éléments du parc.
| Fonctionnalité d'interface | Utilisation au Jurassic Park |
|---|---|
| Définition de base | Structures de données pour les dinosaures, employés, localisations |
| Propriétés optionnelles | Informations supplémentaires qui peuvent être disponibles ou non |
| Propriétés readonly | Données qui ne devraient pas changer, ex. identifiants génétiques |
| Interfaces de fonction | Définitions de procédures et protocoles de sécurité |
| Types indexables | Bases de données dynamiques de dinosaures et de ressources |
| Interfaces de classe | Normes pour les systèmes de sécurité et de surveillance |
| Étendre des interfaces | Héritage de traits entre espèces de dinosaures |
| Fusion de déclarations | Étendre des protocoles avec des procédures supplémentaires |
Le Dr Wu résume : « Les interfaces en TypeScript, comme les protocoles de notre parc, garantissent que chaque élément du système fonctionne comme prévu. Elles nous permettent de construire des systèmes complexes avec la certitude que les différents composants fonctionneront ensemble, ce qui est absolument crucial lorsqu'on a affaire à des espèces qui peuvent être dangereuses si quelque chose tourne mal. »
Code de cette leçon : index.ts
1// Interfaces dans le Parc Jurassique
2console.log("Interfaces TypeScript\n");
3
4// ===========================================
5// 1. Interfaces de base
6// ===========================================
7console.log("=== INTERFACES DE BASE ===");
8
9// Interface simple
10interface Dinosaur {
11 name: string;
12 species: string;
13 age: number;
14 weight: number;
15}
16
17const trex: Dinosaur = {
18 name: "Rexy",
19 species: "Tyrannosaurus Rex",
20 age: 5,
21 weight: 8000
22};
23
24console.log("T-Rex:", trex);
25
26// ===========================================
27// 2. Optional properties
28// ===========================================
29console.log("\n=== OPTIONAL PROPERTIES ===");
30
31interface DinosaurWithOptionals {
32 name: string;
33 species: string;
34 age: number;
35 nickname?: string; // optionnel
36 medicalNotes?: string; // optionnel
37}
38
39const blue: DinosaurWithOptionals = {
40 name: "Blue",
41 species: "Velociraptor",
42 age: 3,
43 nickname: "The Beta"
44 // medicalNotes omitted - OK
45};
46
47console.log("Blue:", blue);
48console.log("Has medical notes:", blue.medicalNotes !== undefined);
49
50// ===========================================
51// 3. Readonly properties
52// ===========================================
53console.log("\n=== READONLY PROPERTIES ===");
54
55interface DinosaurWithReadonly {
56 readonly id: string; // cannot be changed
57 readonly dnaSequence: string; // cannot be changed
58 name: string; // can be changed
59 age: number; // can be changed
60}
61
62const dino: DinosaurWithReadonly = {
63 id: "DINO-001",
64 dnaSequence: "ACGT...",
65 name: "Rex",
66 age: 5
67};
68
69// dino.id = "DINO-002"; // Error: Cannot assign to 'id'
70dino.name = "Rexy"; // OK
71dino.age = 6; // OK
72
73console.log("Dinosaur:", dino);
74
75// ===========================================
76// 4. Méthodes dans les interfaces
77// ===========================================
78console.log("\n=== MÉTHODES DANS LES INTERFACES ===");
79
80interface DinosaurWithMethods {
81 name: string;
82 species: string;
83 energy: number;
84
85 // Méthodes
86 roar(): string;
87 eat(amount: number): void;
88 sleep(hours: number): void;
89 getStatus(): string;
90}
91
92class TyrannosaurusRex implements DinosaurWithMethods {
93 name: string;
94 species: string;
95 energy: number;
96
97 constructor(name: string) {
98 this.name = name;
99 this.species = "Tyrannosaurus Rex";
100 this.energy = 100;
101 }
102
103 roar(): string {
104 this.energy -= 5;
105 return `${this.name} roars: ROAAAAAR!`;
106 }
107
108 eat(amount: number): void {
109 this.energy += amount * 2;
110 if (this.energy > 100) this.energy = 100;
111 console.log(`${this.name} ate ${amount}kg. Energy: ${this.energy}`);
112 }
113
114 sleep(hours: number): void {
115 this.energy += hours * 10;
116 if (this.energy > 100) this.energy = 100;
117 console.log(`${this.name} slept ${hours}h. Energy: ${this.energy}`);
118 }
119
120 getStatus(): string {
121 if (this.energy > 70) return "Energetic";
122 if (this.energy > 30) return "Normal";
123 return "Tired";
124 }
125}
126
127const rexy = new TyrannosaurusRex("Rexy");
128console.log(rexy.roar());
129rexy.eat(50);
130console.log("Status:", rexy.getStatus());
131
132// ===========================================
133// 5. Index signatures
134// ===========================================
135console.log("\n=== INDEX SIGNATURES ===");
136
137// Clés dynamiques
138interface DinosaurCollection {
139 [id: string]: Dinosaur;
140}
141
142const collection: DinosaurCollection = {
143 "T-001": trex,
144 "V-001": blue
145};
146
147console.log("Collection:", collection);
148console.log("T-001:", collection["T-001"]);
149
150// Mixed properties
151interface PaddockData {
152 id: string;
153 capacity: number;
154 [key: string]: any; // other dynamic properties
155}
156
157const paddock: PaddockData = {
158 id: "P-01",
159 capacity: 10,
160 securityLevel: "high",
161 temperature: 25,
162 humidity: 60
163};
164
165console.log("Paddock:", paddock);
166
167// ===========================================
168// 6. Functions dans les interfaces
169// ===========================================
170console.log("\n=== FUNCTIONS DANS LES INTERFACES ===");
171
172// Type function
173interface DinosaurFilter {
174 (dino: Dinosaur): boolean;
175}
176
177const carnivoreFilter: DinosaurFilter = (dino) => {
178 return dino.species.includes("saurus") && dino.weight > 1000;
179};
180
181console.log("Is T-Rex carnivore:", carnivoreFilter(trex));
182
183// Interface for functions with properties
184interface DinosaurValidator {
185 (dino: Dinosaur): boolean;
186 errorMessage: string;
187}
188
189const ageValidator: DinosaurValidator = (dino) => {
190 return dino.age >= 0 && dino.age <= 100;
191};
192ageValidator.errorMessage = "Age must be between 0 and 100";
193
194console.log("Validator:", ageValidator(trex));
195console.log("Error message:", ageValidator.errorMessage);
196
197// ===========================================
198// 7. Nested interfaces
199// ===========================================
200console.log("\n=== NESTED INTERFACES ===");
201
202interface Location {
203 latitude: number;
204 longitude: number;
205 altitude: number;
206}
207
208interface Paddock {
209 id: string;
210 name: string;
211 location: Location; // nested interface
212 dimensions: {
213 width: number;
214 length: number;
215 height: number;
216 };
217 dinosaurs: Dinosaur[];
218}
219
220const trexPaddock: Paddock = {
221 id: "P-01",
222 name: "T-Rex Territory",
223 location: {
224 latitude: 22.3964,
225 longitude: -159.5261,
226 altitude: 50
227 },
228 dimensions: {
229 width: 1000,
230 length: 1500,
231 height: 50
232 },
233 dinosaurs: [trex]
234};
235
236console.log("Paddock:", trexPaddock);
237console.log("Location:", trexPaddock.location);
238
239// ===========================================
240// 8. Hybrid types
241// ===========================================
242console.log("\n=== HYBRID TYPES ===");
243
244interface Counter {
245 (start: number): string;
246 interval: number;
247 reset(): void;
248}
249
250function createCounter(): Counter {
251 let count = 0;
252
253 const counter = function(start: number) {
254 count = start;
255 return `Count: ${count}`;
256 } as Counter;
257
258 counter.interval = 1;
259 counter.reset = function() {
260 count = 0;
261 };
262
263 return counter;
264}
265
266const myCounter = createCounter();
267console.log(myCounter(5));
268console.log("Interval:", myCounter.interval);
269myCounter.reset();
270
271// ===========================================
272// 9. Résumé
273// ===========================================
274console.log("\n=== RÉSUMÉ ===");
275console.log("Interfaces de base - object structure");
276console.log("Optional properties - ?");
277console.log("Readonly - immutable properties");
278console.log("Méthodes - functions dans les interfaces");
279console.log("Index signatures - clés dynamiques");
280console.log("Nested interfaces - complex structures");
281
282console.log("\nInterfaces maîtrisées !");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. En quoi une interface diffère-t-elle d’un alias de type en TypeScript ?
2. Comment marquer une propriété d’interface comme optionnelle en TypeScript ?
Tâches pratiques dans le jeu
- Éditeur de code
Définis une interface Dinosaur avec les champs name: string, species: string, weight?: number et une méthode roar(): void.
- Tri horizontal
Ordonne les éléments d’une définition d’interface TypeScript :
- Clique dans l'ordre
Ordonne les éléments de la définition d’un champ readonly dans une interface :
- Tri vertical
Ordonne les concepts d’interface du plus simple au plus avancé :