Cours JavaScript et TypeScript · Module 5: JavaScript avancé
Classes d'erreurs personnalisées
Dans cette leçon8
Bienvenue à nouveau à Jurassic Park ! Dans les exercices précédents, nous avons découvert les différents types d'erreurs en JavaScript et les bases de leur gestion avec try...catch...finally. Nous allons maintenant approfondir la création de classes d'erreurs personnalisées et non standard, qui sont précieuses dans les grandes applications - comme les systèmes de gestion d'un parc rempli de reptiles préhistoriques.
Pourquoi des classes d'erreurs personnalisées ?
Dans des systèmes complexes comme Jurassic Park, on a besoin d'une catégorisation précise des problèmes. Il ne suffit pas de savoir « quelque chose s'est mal passé » - on doit savoir exactement ce qui s'est produit, où et comment réagir.
Les classes d'erreurs personnalisées permettent de :
- Catégoriser précisément les différents types de problèmes
- Attacher des données contextuelles supplémentaires aux erreurs
- Implémenter des méthodes utilitaires personnalisées
- Filtrer et gérer plus facilement des cas spécifiques
C'est comme la différence entre une alarme générale du parc et un message précis : « Des velociraptors se sont échappés de l'enclos B6, vus pour la dernière fois en direction du centre d'accueil. »
Créer une hiérarchie d'erreurs personnalisée
Pour Jurassic Park, nous allons créer une hiérarchie d'erreurs complète qui reflète le domaine :
1// Classe d'erreur de base pour tous les problèmes du parc
2class JurassicParkError extends Error {
3 constructor(message, code, severity = "medium") {
4 super(message);
5 this.name = "JurassicParkError";
6 this.code = code;
7 this.severity = severity;
8 this.timestamp = new Date().toISOString();
9
10 // Préserve la trace de la pile
11 if (Error.captureStackTrace) {
12 Error.captureStackTrace(this, this.constructor);
13 }
14 }
15
16 toJSON() {
17 return {
18 name: this.name,
19 message: this.message,
20 code: this.code,
21 severity: this.severity,
22 timestamp: this.timestamp,
23 stack: this.stack
24 };
25 }
26
27 toString() {
28 return `[${this.severity.toUpperCase()}] ${this.name} (${this.code}) : ${this.message}`;
29 }
30}
31
32// Erreurs liées aux dinosaures
33class DinosaurError extends JurassicParkError {
34 constructor(message, code, dinosaurId, species, severity = "high") {
35 super(message, code, severity);
36 this.name = "DinosaurError";
37 this.dinosaurId = dinosaurId;
38 this.species = species;
39 }
40}
41
42class DinosaurHealthError extends DinosaurError {
43 constructor(dinosaurId, species, healthIssue, vitalSigns) {
44 super(
45 `Problème de santé chez ${species} (ID : ${dinosaurId}) : ${healthIssue}`,
46 "DINO_HEALTH_ISSUE",
47 dinosaurId,
48 species,
49 "medium"
50 );
51 this.name = "DinosaurHealthError";
52 this.healthIssue = healthIssue;
53 this.vitalSigns = vitalSigns;
54 }
55}
56
57class DinosaurEscapeError extends DinosaurError {
58 constructor(dinosaurId, species, lastKnownLocation) {
59 super(
60 `${species} (ID : ${dinosaurId}) s'est échappé ! Dernier emplacement connu : ${lastKnownLocation}`,
61 "DINO_ESCAPE",
62 dinosaurId,
63 species,
64 "critical"
65 );
66 this.name = "DinosaurEscapeError";
67 this.lastKnownLocation = lastKnownLocation;
68 }
69}
70
71// Erreurs d'infrastructure
72class ParkInfrastructureError extends JurassicParkError {
73 constructor(message, code, system, severity = "high") {
74 super(message, code, severity);
75 this.name = "ParkInfrastructureError";
76 this.system = system;
77 }
78}
79
80class PowerSystemError extends ParkInfrastructureError {
81 constructor(sector, powerLevel, backupAvailable) {
82 super(
83 `Panne de courant dans le secteur ${sector}. Niveau : ${powerLevel}%. Secours : ${backupAvailable ? 'disponible' : 'indisponible'}`,
84 "POWER_FAILURE",
85 "power",
86 backupAvailable ? "medium" : "critical"
87 );
88 this.name = "PowerSystemError";
89 this.sector = sector;
90 this.powerLevel = powerLevel;
91 this.backupAvailable = backupAvailable;
92 }
93}
94
95class FenceSystemError extends ParkInfrastructureError {
96 constructor(sector, fenceId, integrity) {
97 super(
98 `Panne de clôture dans le secteur ${sector} : ${fenceId}, intégrité : ${integrity}%`,
99 "FENCE_FAILURE",
100 "fence",
101 integrity < 50 ? "critical" : "high"
102 );
103 this.name = "FenceSystemError";
104 this.sector = sector;
105 this.fenceId = fenceId;
106 this.integrity = integrity;
107 }
108}Utiliser la hiérarchie d'erreurs
1// Système de gestion des incidents
2class IncidentManagementSystem {
3 constructor() {
4 this.incidents = [];
5 this.alertLevel = "green";
6 }
7
8 reportIncident(error) {
9 const incident = {
10 id: `INC-${Date.now()}`,
11 error: error.toJSON(),
12 status: "open",
13 reportedAt: new Date().toISOString(),
14 actions: []
15 };
16
17 this.incidents.push(incident);
18 this._updateAlertLevel(error);
19 this._dispatchResponse(error, incident.id);
20
21 return incident.id;
22 }
23
24 _updateAlertLevel(error) {
25 const severityOrder = { low: 0, medium: 1, high: 2, critical: 3 };
26 const currentLevel = this.alertLevel;
27
28 if (error.severity === "critical") {
29 this.alertLevel = "red";
30 } else if (error.severity === "high" && currentLevel !== "red") {
31 this.alertLevel = "orange";
32 } else if (error.severity === "medium" && !["red", "orange"].includes(currentLevel)) {
33 this.alertLevel = "yellow";
34 }
35
36 if (this.alertLevel !== currentLevel) {
37 console.log(`Niveau d'alerte modifié : ${currentLevel} → ${this.alertLevel}`);
38 }
39 }
40
41 _dispatchResponse(error, incidentId) {
42 if (error instanceof DinosaurEscapeError) {
43 console.log(`CRITIQUE : Activation du protocole d'urgence pour l'évasion de ${error.species} !`);
44 console.log(`Dernier emplacement connu : ${error.lastKnownLocation}`);
45 // Active le confinement, notifie les équipes de sécurité, etc.
46 } else if (error instanceof PowerSystemError) {
47 console.log(`Panne de courant dans le secteur ${error.sector}`);
48 if (!error.backupAvailable) {
49 console.log("CRITIQUE : Pas d'alimentation de secours ! Les clôtures peuvent être inactives !");
50 }
51 } else if (error instanceof FenceSystemError) {
52 console.log(`Panne de clôture : secteur ${error.sector}, intégrité ${error.integrity}%`);
53 if (error.integrity < 50) {
54 console.log("CRITIQUE : Intégrité des clôtures critique - risque de brèche potentielle !");
55 }
56 } else if (error instanceof DinosaurHealthError) {
57 console.log(`Alerte santé pour ${error.species} : ${error.healthIssue}`);
58 } else {
59 console.log(`Incident général signalé : ${error.message}`);
60 }
61 }
62
63 getIncidentById(id) {
64 return this.incidents.find(i => i.id === id);
65 }
66
67 resolveIncident(id, resolution) {
68 const incident = this.getIncidentById(id);
69 if (incident) {
70 incident.status = "resolved";
71 incident.resolvedAt = new Date().toISOString();
72 incident.resolution = resolution;
73 }
74 return incident;
75 }
76}
77
78// Exemple d'utilisation
79const incidentSystem = new IncidentManagementSystem();
80
81// Simule une évasion de dinosaure
82try {
83 const fenceIntegrity = 20; // 20% - très bas !
84 if (fenceIntegrity < 30) {
85 throw new FenceSystemError("B6", "FENCE-B6-NORTH", fenceIntegrity);
86 }
87} catch (error) {
88 const incidentId = incidentSystem.reportIncident(error);
89 console.log(`Incident enregistré : ${incidentId}`);
90 console.log(error.toString());
91}
92
93// Simule un problème de santé d'un dinosaure
94try {
95 const vitalSigns = { heartRate: 180, temperature: 41.5, respiratory: 45 };
96 if (vitalSigns.temperature > 41) {
97 throw new DinosaurHealthError("VEL-02", "Velociraptor", "Forte fièvre", vitalSigns);
98 }
99} catch (error) {
100 const incidentId = incidentSystem.reportIncident(error);
101 console.log(`Incident santé : ${incidentId}`);
102}Sérialisation des erreurs
L'un des problèmes des objets d'erreur en JavaScript est leur sérialisation. Par défaut, quand on essaie de convertir une erreur en JSON, on perd la plupart des informations :
1const error = new Error('Erreur de test');
2console.log(JSON.stringify(error)); // {} - objet vide !Les classes d'erreurs personnalisées peuvent résoudre ce problème en implémentant une méthode toJSON :
1class SerializableError extends Error {
2 constructor(message) {
3 super(message);
4 this.name = this.constructor.name;
5 }
6
7 toJSON() {
8 return {
9 name: this.name,
10 message: this.message,
11 stack: this.stack,
12 // Propriétés supplémentaires spécifiques aux sous-classes
13 ...Object.getOwnPropertyNames(this)
14 .filter(prop => !['name', 'message', 'stack'].includes(prop))
15 .reduce((obj, prop) => {
16 obj[prop] = this[prop];
17 return obj;
18 }, {})
19 };
20 }
21}
22
23// On peut maintenant créer des erreurs sérialisables
24class DinosaurEscapeError extends SerializableError {
25 constructor(message, species, location) {
26 super(message);
27 this.species = species;
28 this.location = location;
29 this.timestamp = new Date();
30 }
31}
32
33const error = new DinosaurEscapeError(
34 "Dinosaure échappé de l'enclos",
35 'Velociraptor',
36 'Sector B6'
37);
38
39// L'erreur peut maintenant être correctement sérialisée
40console.log(JSON.stringify(error, null, 2));
41/* Sortie :
42{
43 "name": "DinosaurEscapeError",
44 "message": "Dinosaure échappé de l'enclos",
45 "stack": "DinosaurEscapeError: Dinosaure échappé de l'enclos\n at ...",
46 "species": "Velociraptor",
47 "location": "Sector B6",
48 "timestamp": "2023-05-15T10:30:45.123Z"
49}
50*/C'est particulièrement utile quand on doit envoyer des informations d'erreur via une API ou les enregistrer dans des logs.
Gérer les erreurs asynchrones avec des classes personnalisées
Les classes d'erreurs personnalisées fonctionnent très bien avec le code asynchrone :
1// Classe d'erreur spécifique à l'API
2class APIError extends Error {
3 constructor(message, statusCode, endpoint) {
4 super(message);
5 this.name = 'APIError';
6 this.statusCode = statusCode;
7 this.endpoint = endpoint;
8 this.timestamp = new Date();
9 }
10
11 isServerError() {
12 return this.statusCode >= 500;
13 }
14
15 isClientError() {
16 return this.statusCode >= 400 && this.statusCode < 500;
17 }
18
19 isAuthError() {
20 return this.statusCode === 401 || this.statusCode === 403;
21 }
22}
23
24// Fonction de récupération de données depuis l'API qui utilise une classe d'erreur personnalisée
25async function fetchDinosaurData(dinoId) {
26 try {
27 const response = await fetch(`https://jurassic-park-api.com/dinosaurs/${dinoId}`);
28
29 if (!response.ok) {
30 throw new APIError(
31 `Erreur de récupération des données du dinosaure #${dinoId}`,
32 response.status,
33 `/dinosaurs/${dinoId}`
34 );
35 }
36
37 return await response.json();
38 } catch (error) {
39 if (error instanceof APIError) {
40 // Gère les erreurs d'API
41 if (error.isAuthError()) {
42 await refreshAuthToken();
43 return fetchDinosaurData(dinoId); // Réessaie après avoir rafraîchi le token
44 } else if (error.isServerError()) {
45 console.error("Erreur serveur, tentative avec l'API de secours...");
46 return fetchDinosaurDataFromBackupAPI(dinoId);
47 } else {
48 console.error(`Erreur client : ${error.message} (statusCode : ${error.statusCode})`);
49 throw error; // Relance l'erreur
50 }
51 } else {
52 // Autre type d'erreur (par ex. erreur réseau)
53 console.error('Erreur inattendue :', error);
54 throw new Error(`Échec de la récupération des données du dinosaure #${dinoId} : ${error.message}`);
55 }
56 }
57}Exemple avancé : système de signalement d'incidents
Voyons comment utiliser les classes d'erreurs personnalisées dans un exemple plus élaboré - un système de signalement d'incidents pour Jurassic Park :
1// Classe d'erreur de base pour les incidents
2class IncidentError extends Error {
3 constructor(message, location, severity) {
4 super(message);
5 this.name = 'IncidentError';
6 this.location = location;
7 this.severity = severity; // 1-10
8 this.timestamp = new Date();
9 this.resolved = false;
10 this.resolutionTime = null;
11 }
12
13 markAsResolved() {
14 this.resolved = true;
15 this.resolutionTime = new Date();
16 return this;
17 }
18
19 getIncidentDuration() {
20 if (!this.resolved) return null;
21 return (this.resolutionTime - this.timestamp) / 1000; // en secondes
22 }
23
24 requiresEvacuation() {
25 return this.severity >= 7;
26 }
27
28 requiresNotification() {
29 return this.severity >= 4;
30 }
31
32 getSummary() {
33 return {
34 type: this.name,
35 message: this.message,
36 location: this.location,
37 severity: this.severity,
38 timestamp: this.timestamp,
39 status: this.resolved ? 'Résolu' : 'Actif',
40 resolutionTime: this.resolutionTime
41 };
42 }
43
44 toJSON() {
45 return this.getSummary();
46 }
47}
48
49// Classes pour différents types d'incidents
50class DinosaurEscapeIncident extends IncidentError {
51 constructor(message, location, species, count, dangerLevel) {
52 super(message, location, dangerLevel);
53 this.name = 'DinosaurEscapeIncident';
54 this.species = species;
55 this.count = count;
56 this.lastSighting = location;
57 }
58
59 updateSighting(newLocation) {
60 this.lastSighting = newLocation;
61 return this;
62 }
63
64 getSummary() {
65 return {
66 ...super.getSummary(),
67 species: this.species,
68 count: this.count,
69 lastSighting: this.lastSighting
70 };
71 }
72}
73
74class SystemFailureIncident extends IncidentError {
75 constructor(message, location, system, affectedAreas, backupStatus) {
76 // Calcule le niveau de criticité selon le système et la disponibilité du secours
77 const severity = SystemFailureIncident.calculateSeverity(system, backupStatus);
78
79 super(message, location, severity);
80 this.name = 'SystemFailureIncident';
81 this.system = system;
82 this.affectedAreas = affectedAreas;
83 this.backupStatus = backupStatus;
84 }
85
86 static calculateSeverity(system, backupStatus) {
87 const systemCriticality = {
88 'electric_fences': 10,
89 'security_doors': 9,
90 'surveillance': 7,
91 'climate_control': 6,
92 'guest_services': 3
93 };
94
95 // Si le secours fonctionne, réduit le niveau de criticité
96 const baseSeverity = systemCriticality[system] || 5;
97 return backupStatus === 'operational' ? Math.max(3, baseSeverity - 3) : baseSeverity;
98 }
99
100 requiresMaintenanceTeam() {
101 return true;
102 }
103
104 requiresEmergencyPower() {
105 return this.system === 'electric_fences' || this.system === 'security_doors';
106 }
107
108 getSummary() {
109 return {
110 ...super.getSummary(),
111 system: this.system,
112 affectedAreas: this.affectedAreas,
113 backupStatus: this.backupStatus,
114 requiresEmergencyPower: this.requiresEmergencyPower()
115 };
116 }
117}
118
119class VisitorIncident extends IncidentError {
120 constructor(message, location, visitorCount, injuryLevel, medicalStatus) {
121 // Calcule le niveau de criticité selon le nombre de visiteurs et le niveau de blessure
122 const severity = VisitorIncident.calculateSeverity(visitorCount, injuryLevel);
123
124 super(message, location, severity);
125 this.name = 'VisitorIncident';
126 this.visitorCount = visitorCount;
127 this.injuryLevel = injuryLevel; // 'none', 'minor', 'moderate', 'severe'
128 this.medicalStatus = medicalStatus; // 'not_required', 'dispatched', 'on_scene', 'evacuating'
129 }
130
131 static calculateSeverity(visitorCount, injuryLevel) {
132 const injurySeverity = {
133 'none': 1,
134 'minor': 3,
135 'moderate': 6,
136 'severe': 9
137 };
138
139 // Augmente le niveau de criticité pour les groupes de visiteurs plus importants
140 const baseSeverity = injurySeverity[injuryLevel] || 5;
141 const visitorFactor = visitorCount > 10 ? 2 : visitorCount > 3 ? 1 : 0;
142
143 return Math.min(10, baseSeverity + visitorFactor);
144 }
145
146 requiresMedicalTeam() {
147 return this.injuryLevel !== 'none';
148 }
149
150 updateMedicalStatus(newStatus) {
151 this.medicalStatus = newStatus;
152
153 // Si l'aide médicale est sur place et que la situation n'est pas critique,
154 // on peut réduire le niveau de criticité
155 if (newStatus === 'on_scene' && this.injuryLevel !== 'severe') {
156 this.severity = Math.max(2, this.severity - 2);
157 }
158
159 return this;
160 }
161
162 getSummary() {
163 return {
164 ...super.getSummary(),
165 visitorCount: this.visitorCount,
166 injuryLevel: this.injuryLevel,
167 medicalStatus: this.medicalStatus,
168 requiresMedicalTeam: this.requiresMedicalTeam()
169 };
170 }
171}
172
173// Système de gestion des incidents
174class IncidentManagementSystem {
175 constructor() {
176 this.activeIncidents = new Map();
177 this.resolvedIncidents = [];
178 this.eventListeners = {
179 'new': [],
180 'update': [],
181 'resolve': []
182 };
183 }
184
185 reportIncident(incident) {
186 if (!(incident instanceof IncidentError)) {
187 throw new TypeError('Seuls les objets IncidentError peuvent être signalés');
188 }
189
190 const incidentId = `INC-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
191 this.activeIncidents.set(incidentId, incident);
192
193 // Notifie tous les écouteurs du nouvel incident
194 this._notifyListeners('new', { id: incidentId, incident });
195
196 // Déclenche les réponses automatisées
197 this._triggerAutomatedResponses(incidentId, incident);
198
199 return incidentId;
200 }
201
202 updateIncident(incidentId, updateFn) {
203 if (!this.activeIncidents.has(incidentId)) {
204 throw new Error(`L'incident avec l'ID ${incidentId} n'existe pas ou a déjà été résolu`);
205 }
206
207 const incident = this.activeIncidents.get(incidentId);
208 updateFn(incident);
209
210 // Notifie tous les écouteurs de la mise à jour de l'incident
211 this._notifyListeners('update', { id: incidentId, incident });
212
213 return incident;
214 }
215
216 resolveIncident(incidentId, resolutionNotes = '') {
217 if (!this.activeIncidents.has(incidentId)) {
218 throw new Error(`L'incident avec l'ID ${incidentId} n'existe pas ou a déjà été résolu`);
219 }
220
221 const incident = this.activeIncidents.get(incidentId);
222 incident.markAsResolved();
223 incident.resolutionNotes = resolutionNotes;
224
225 // Déplace l'incident des actifs vers les résolus
226 this.activeIncidents.delete(incidentId);
227 this.resolvedIncidents.push({ id: incidentId, incident });
228
229 // Notifie tous les écouteurs de la résolution
230 this._notifyListeners('resolve', { id: incidentId, incident });
231
232 return incident;
233 }
234
235 getActiveIncidents() {
236 return Array.from(this.activeIncidents.entries()).map(([id, incident]) => ({
237 id,
238 ...incident.getSummary()
239 }));
240 }
241
242 addEventListener(event, callback) {
243 if (!this.eventListeners[event]) {
244 throw new Error(`Type d'événement inconnu : ${event}`);
245 }
246
247 this.eventListeners[event].push(callback);
248 return () => this.removeEventListener(event, callback);
249 }
250
251 removeEventListener(event, callback) {
252 if (!this.eventListeners[event]) return false;
253
254 const index = this.eventListeners[event].indexOf(callback);
255 if (index !== -1) {
256 this.eventListeners[event].splice(index, 1);
257 return true;
258 }
259 return false;
260 }
261
262 _notifyListeners(event, data) {
263 if (!this.eventListeners[event]) return;
264
265 for (const listener of this.eventListeners[event]) {
266 try {
267 listener(data);
268 } catch (error) {
269 console.error(`Erreur dans l'écouteur d'événement ${event} :`, error);
270 }
271 }
272 }
273
274 _triggerAutomatedResponses(incidentId, incident) {
275 console.log(`Réponse automatisée pour l'incident ${incidentId} :`, incident.name);
276
277 // Réactions différentes selon le type d'incident
278 if (incident instanceof DinosaurEscapeIncident) {
279 if (incident.requiresEvacuation()) {
280 console.log(`ÉVACUATION ! Activation du protocole d'évacuation pour : ${incident.location}`);
281 }
282
283 console.log(`Envoi de l'ACU (Asset Containment Unit) à l'emplacement : ${incident.lastSighting}`);
284 }
285
286 else if (incident instanceof SystemFailureIncident) {
287 console.log(`Notification de l'équipe de maintenance concernant la panne système : ${incident.system}`);
288
289 if (incident.requiresEmergencyPower()) {
290 console.log("Activation du système d'alimentation de secours !");
291 }
292 }
293
294 else if (incident instanceof VisitorIncident) {
295 if (incident.requiresMedicalTeam()) {
296 console.log(`Envoi de l'équipe médicale à l'emplacement : ${incident.location}`);
297
298 // Met à jour l'état médical
299 this.updateIncident(incidentId, inc => inc.updateMedicalStatus('dispatched'));
300 }
301
302 if (incident.requiresEvacuation()) {
303 console.log(`Démarrage de l'évacuation des visiteurs depuis : ${incident.location}`);
304 }
305 }
306
307 // Réactions communes à tous les incidents
308 if (incident.requiresNotification()) {
309 console.log('Notification de la direction du parc...');
310 }
311 }
312}
313
314// Exemple d'utilisation du système d'incidents
315function demoParkIncidentSystem() {
316 const incidentSystem = new IncidentManagementSystem();
317
318 // Ajoute un écouteur pour les nouveaux incidents
319 incidentSystem.addEventListener('new', ({ id, incident }) => {
320 console.log(`NOUVEL INCIDENT [${id}] : ${incident.name} à l'emplacement ${incident.location}`);
321 });
322
323 // Signale un incident de dinosaure
324 const raptorIncidentId = incidentSystem.reportIncident(
325 new DinosaurEscapeIncident(
326 "Évasion de velociraptor de l'enclos",
327 'Sector C4',
328 'Velociraptor',
329 3,
330 9 // Niveau de danger élevé
331 )
332 );
333
334 // Met à jour l'incident - change le dernier emplacement vu
335 setTimeout(() => {
336 incidentSystem.updateIncident(raptorIncidentId, incident => {
337 incident.updateSighting("Sector C5 - près du centre d'accueil");
338 console.log(`Mise à jour : Raptors repérés à ${incident.lastSighting}`);
339 });
340 }, 2000);
341
342 // Signale un incident système
343 const powerIncidentId = incidentSystem.reportIncident(
344 new SystemFailureIncident(
345 'Panne de courant des clôtures électriques',
346 'Sector B1-B3',
347 'electric_fences',
348 ['TRex Paddock', 'Raptor Containment'],
349 'offline' // Le secours ne fonctionne pas
350 )
351 );
352
353 // Signale un incident visiteur
354 const visitorIncidentId = incidentSystem.reportIncident(
355 new VisitorIncident(
356 "Visiteurs hors de l'itinéraire de visite prévu",
357 'Sector D2',
358 4, // Nombre de visiteurs
359 'minor', // Blessures mineures
360 'not_required' // État médical
361 )
362 );
363
364 // Affiche les incidents actifs
365 setTimeout(() => {
366 console.log('\n--- INCIDENTS ACTIFS ---');
367 console.table(incidentSystem.getActiveIncidents());
368 }, 3000);
369
370 // Résout l'incident visiteur
371 setTimeout(() => {
372 incidentSystem.resolveIncident(
373 visitorIncidentId,
374 "Visiteurs raccompagnés en sécurité sur l'itinéraire de visite prévu."
375 );
376 console.log(`Incident ${visitorIncidentId} marqué comme résolu.`);
377 }, 4000);
378
379 // Résout l'incident de courant
380 setTimeout(() => {
381 incidentSystem.resolveIncident(
382 powerIncidentId,
383 'Courant rétabli, clôtures à nouveau actives.'
384 );
385 console.log(`Incident ${powerIncidentId} marqué comme résolu.`);
386 }, 5000);
387
388 // Résout l'incident des raptors
389 setTimeout(() => {
390 incidentSystem.resolveIncident(
391 raptorIncidentId,
392 'Les 3 velociraptors capturés et transportés en sécurité vers leur enclos.'
393 );
394 console.log(`Incident ${raptorIncidentId} marqué comme résolu.`);
395 }, 6000);
396}
397
398// Lance la démo
399// demoParkIncidentSystem();Bonnes pratiques pour créer des classes d'erreurs personnalisées
- Nomme les erreurs de façon explicite - le nom doit indiquer clairement ce qui s'est mal passé
- Crée une hiérarchie d'erreurs - utilise l'héritage pour créer une hiérarchie logique de classes d'erreurs
- Ajoute du contexte - attache toujours des informations supplémentaires qui aideront à diagnostiquer et gérer l'erreur
- Implémente des méthodes utilitaires - ajoute des méthodes qui facilitent la catégorisation et la gestion des erreurs
- Assure la sérialisabilité - ajoute une méthode
toJSON()pour que les erreurs puissent être facilement journalisées et transmises - Préserve la trace de la pile - assure-toi d'appeler
super(message)dans le constructeur pour préserver la trace de la pile - Documente tes classes d'erreurs - les autres développeurs (et toi à l'avenir) doivent savoir quand et comment utiliser tes classes d'erreurs
Conclusions
Les classes d'erreurs personnalisées sont un outil puissant dans l'arsenal du développeur JavaScript. Dans des applications complexes comme le système de gestion de Jurassic Park, la catégorisation précise et la gestion des différents types d'erreurs sont essentielles pour maintenir la sécurité et la stabilité.
Souviens-toi, comme l'a dit le Dr Ian Malcolm : « La vie... euh... trouve toujours un chemin. » C'est pareil pour les erreurs dans le code. La meilleure approche n'est pas de faire comme si les erreurs n'existaient pas, mais de s'attendre à elles et d'être prêt à les gérer de la façon la plus élégante et efficace possible.
Dans l'exercice suivant, nous utiliserons toutes les techniques apprises pour implémenter une application complète démontrant les modules, l'asynchronie et la gestion des erreurs en pratique.
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. Comment crée-t-on correctement une classe d'erreur personnalisée en JavaScript ?
2. À quoi sert l'opérateur instanceof dans la gestion des erreurs ?
Tâches pratiques dans le jeu
- Éditeur de code
Implémente les classes ParkError, FenceBreachError et PowerFailureError avec des méthodes utilitaires.
- Clique dans l'ordre
Ordonne correctement les éléments de la définition d'une classe d'erreur personnalisée :