Cours JavaScript et TypeScript · Module 5: JavaScript avancé

Classes d'erreurs personnalisées

14 min de lecture
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 :

  1. Catégoriser précisément les différents types de problèmes
  2. Attacher des données contextuelles supplémentaires aux erreurs
  3. Implémenter des méthodes utilitaires personnalisées
  4. 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

  1. Nomme les erreurs de façon explicite - le nom doit indiquer clairement ce qui s'est mal passé
  2. Crée une hiérarchie d'erreurs - utilise l'héritage pour créer une hiérarchie logique de classes d'erreurs
  3. Ajoute du contexte - attache toujours des informations supplémentaires qui aideront à diagnostiquer et gérer l'erreur
  4. Implémente des méthodes utilitaires - ajoute des méthodes qui facilitent la catégorisation et la gestion des erreurs
  5. Assure la sérialisabilité - ajoute une méthode toJSON() pour que les erreurs puissent être facilement journalisées et transmises
  6. Préserve la trace de la pile - assure-toi d'appeler super(message) dans le constructeur pour préserver la trace de la pile
  7. 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. 1. Comment crée-t-on correctement une classe d'erreur personnalisée en JavaScript ?

  2. 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 :

Articles utiles