Cours JavaScript et TypeScript · Module 2: Fonctions, tableaux et objets

La gestion des erreurs en JavaScript

17 min de lecture
Dans cette leçon7

À Jurassic Park, même les systèmes les plus soigneusement planifiés finissent par lâcher - un dinosaure s'échappe de son enclos, un capteur cesse d'envoyer ses relevés, une mangeoire automatique reçoit des données corrompues. C'est précisément pour cela que chaque système de sécurité du parc a ses protocoles d'urgence : la première panne ne ferme pas le parc, elle déclenche une procédure. En JavaScript, cette procédure, c'est le mécanisme try/catch/finally.

Le bloc try/catch - le protocole de sécurité

Imagine que chaque opération dans le parc est une menace potentielle. Le bloc try est la zone dans laquelle nous exécutons l'opération risquée, et catch l'équipe de secours qui intervient quand quelque chose tourne mal. L'exemple type d'une telle opération risquée est le parsing d'un JSON, car une transmission venant d'un capteur peut arriver corrompue.

1try {
2  // Zone à risque - parsing des données du capteur
3  const dinoData = JSON.parse('{ JSON invalide }');
4  console.log(dinoData.species);
5} catch (error) {
6  // Équipe de secours - gestion de l'erreur
7  console.log("ALARME ! Panne du système :", error.message);
8}
9console.log("Le système continue de fonctionner - le parc n'a pas été fermé !");

Sans try/catch, le programme s'arrêterait définitivement sur cette erreur - comme s'il fallait évacuer tout le parc à cause d'un seul capteur défectueux. Ici, l'erreur atterrit dans l'objet error, le bloc catch écrit son message dans la console, et le programme poursuit son exécution juste après toute la construction. C'est pourquoi la dernière ligne s'affiche malgré tout.

Regarde de plus près par quel chemin l'erreur nous parvient. JSON.parse ne renvoie aucune valeur d'erreur que l'on pourrait comparer avec ===, donc une écriture du genre if (JSON.parse(data) === error) n'a aucune chance de fonctionner. Il n'y a pas non plus de callback .onError() que l'on pourrait enregistrer, et le résultat du parsing n'est pas une promesse (promise) - tu ne peux pas y accrocher .catch(), et encore moins inverser cette écriture sous la forme error.catch(JSON.parse(data)). Seul le bloc catch peut intercepter une erreur levée. La seule façon correcte de gérer une erreur pendant le parsing d'un JSON ressemble donc à ceci : try { JSON.parse(data); } catch (error) { handleError(error); }.

La construction elle-même a toujours la même forme et se compose de cinq parties, écrites exactement dans cet ordre : le mot-clé try, le bloc entre accolades avec le code risqué { riskyCode(); }, le mot-clé catch, le paramètre (error) entre parenthèses, et enfin le bloc de traitement { handleError(error); }. Les parenthèses avec le paramètre appartiennent à catch, jamais à try - c'est l'erreur la plus fréquente lors des premiers essais. Retiens ce squelette en cinq éléments une bonne fois pour toutes, et tu le reproduiras ensuite de mémoire à chaque appel risqué.

Le bloc finally - le protocole final

finally est un bloc qui s'exécute toujours - qu'une erreur se soit produite ou non. C'est la procédure de fermeture de la clôture : elle s'applique quel que soit le résultat de la mission, et dans le code, elle correspond à tout ce qu'il ne faut pas oublier, même en pleine panique.

1function feedDinosaur(dinoName, food) {
2  console.log("Ouverture de la mangeoire pour " + dinoName + "...");
3
4  try {
5    if (!food) {
6      throw new Error("Aucune nourriture dans le système !");
7    }
8    console.log("Distribution de " + food + " pour " + dinoName);
9  } catch (error) {
10    console.log("ALARME : " + error.message);
11  } finally {
12    // On ferme TOUJOURS la mangeoire - que le nourrissage ait réussi ou non
13    console.log("Fermeture de la mangeoire - la sécurité avant tout !");
14  }
15}
16
17feedDinosaur("Rex", "viande");  // Succès + fermeture
18feedDinosaur("Rex", null);      // Erreur + fermeture (finally s'exécute toujours !)

Compare les deux appels. Le premier nourrissage réussit, donc catch n'entre pas du tout en jeu - et la mangeoire est quand même fermée. Le second lève une erreur, catch donne l'alarme - et la mangeoire est de nouveau fermée. C'est là que naît le malentendu le plus fréquent : finally n'est pas un bloc qui s'exécute seulement quand une erreur se produit dans le bloc try, ni un bloc qui s'exécute seulement quand aucune erreur ne se produit, ni un bloc qui dépend du fait que le bloc catch a réussi à gérer l'erreur. Il passe à chaque fois, y compris quand un return apparaît dans try.

Tout le cycle de gestion d'une erreur passe par cinq étapes :

  1. Le code du bloc try rencontre une erreur
  2. L'exécution passe au bloc catch avec l'objet error
  3. Le bloc catch gère l'erreur (journalisation, réparation)
  4. Le bloc finally effectue les opérations finales (nettoyage des ressources)
  5. Le programme poursuit son exécution après le bloc try/catch/finally

C'est donc dans la quatrième étape que nous plaçons tout ce qu'il faut libérer, quel que soit le déroulement de la mission : les connexions, les fichiers et les mangeoires que quelqu'un a laissées ouvertes.

Les classes d'erreurs en JavaScript

JavaScript possède une hiérarchie d'erreurs intégrée, tout comme le parc a différents niveaux d'alerte pour différentes menaces.

Les principaux types d'erreurs

  • Error - une erreur générale (l'alerte générale du parc)
  • TypeError - une opération sur un type de données inapproprié (nourrir un herbivore avec de la viande)
  • RangeError - une valeur hors de la plage autorisée (une température d'incubateur hors échelle)
  • ReferenceError - une référence à une variable inexistante (un dinosaure absent de la base)
  • SyntaxError - une erreur de syntaxe dans le code (un plan d'ADN corrompu)

Tu reconnaîtras chacune de ces classes à l'intérieur de catch grâce à l'opérateur instanceof, et c'est justement ce test qui décide quelle procédure d'urgence lancer - le parc doit savoir quelle alarme s'est déclenchée avant d'envoyer une équipe.

1// TypeError - opération sur un type inapproprié
2try {
3  const dino = null;
4  dino.roar(); // null n'a pas de méthodes !
5} catch (error) {
6  console.log(error instanceof TypeError); // true
7  console.log(error.message); // "Cannot read properties of null"
8}
9
10// RangeError - valeur hors plage
11try {
12  const dinoArray = new Array(-5); // Longueur de tableau négative !
13} catch (error) {
14  console.log(error instanceof RangeError); // true
15}
16
17// ReferenceError - variable inexistante
18try {
19  console.log(missingDinosaur);
20} catch (error) {
21  console.log(error instanceof ReferenceError); // true
22}

Trois pannes différentes, trois classes différentes, et c'est la même forme de catch qui les gère. La propriété message porte une description lisible par un humain, tandis que instanceof fournit une catégorie en fonction de laquelle le programme peut bifurquer. Toutes les trois apparaissent pendant l'exécution du programme, et c'est grâce à cela que try/catch a une chance de les intercepter.

SyntaxError échappe à cette règle, car le moteur repère une syntaxe cassée dans ton propre fichier avant même que le programme ne démarre. Pendant l'exécution, tu le rencontreras à deux endroits : à l'intérieur de eval et - bien plus souvent - lors du parsing d'un JSON corrompu.

1// SyntaxError - code corrompu, généralement repéré avant le démarrage du programme
2try {
3  eval("function(");
4} catch (error) {
5  console.log(error instanceof SyntaxError); // true
6}
7
8// La même classe revient avec un JSON corrompu
9try {
10  JSON.parse("{ JSON invalide }");
11} catch (error) {
12  console.log(error instanceof SyntaxError); // true
13}

Le second exemple boucle la boucle avec notre premier fragment : l'alarme levée par JSON.parse est un SyntaxError. Grâce à cela, tu distingueras dans le code une transmission arrivée corrompue d'une transmission arrivée entière, mais qui contient des valeurs absurdes. Ce sont deux pannes complètement différentes, qui exigent deux réactions différentes de l'opérateur de garde.

Throw - le déclenchement de l'alarme

Le mot-clé throw permet de provoquer volontairement une erreur - comme déclencher l'alarme du parc à la main au moment où nous remarquons un problème, avant que les systèmes automatiques n'aient eu le temps de le détecter. Une règle d'or l'accompagne : lance un objet Error ou une de ses sous-classes, jamais une simple chaîne de caractères, car seul un objet d'erreur transporte un message, un nom et la trace de la pile d'appels.

1function checkEnclosureFence(voltage) {
2  if (voltage < 0) {
3    throw new RangeError("La tension ne peut pas être négative : " + voltage);
4  }
5  if (voltage < 5000) {
6    throw new Error("ALARME : tension de la clôture sous le minimum ! (" + voltage + "V)");
7  }
8  console.log("Clôture opérationnelle : " + voltage + "V");
9}
10
11try {
12  checkEnclosureFence(10000); // OK
13  checkEnclosureFence(3000);  // Lance un Error
14} catch (error) {
15  console.log("Problème détecté :", error.message);
16}

Deux protections, deux classes différentes : une mesure négative est physiquement impossible, elle reçoit donc un RangeError, et une clôture simplement trop faible - un Error ordinaire. Dès que throw agit, le reste de la fonction est abandonné : le second appel n'affichera jamais le message de clôture opérationnelle, le contrôle saute directement dans catch, et une éventuelle troisième instruction du bloc try ne s'exécutera pas non plus. La fonction ne tranche rien - elle se contente de signaler, et c'est l'appelant qui prend la décision. Tu utiliseras le même mécanisme pour valider des données : quand une fonction de recherche ne trouve pas un dinosaure, lancer un Error est bien plus sûr que renvoyer discrètement undefined, et lors du nourrissage, chaque règle enfreinte - régime alimentaire inconnu, niveau de santé impossible, stock de nourriture vide - déclenche sa propre alarme.

Re-throw - transmettre l'alarme plus haut

Il arrive que catch intercepte une erreur dont il ne sait rien faire lui-même. Il la relance alors (re-throw), pour que quelqu'un de plus haut dans la chaîne de commandement réagisse. Attraper une erreur uniquement pour qu'elle disparaisse en silence est la pire solution possible : l'alarme se tait, mais le dinosaure court toujours en liberté.

1function processEnclosureData(data) {
2  try {
3    const parsed = JSON.parse(data);
4    if (!parsed.species) {
5      throw new Error("Espèce manquante dans les données de l'enclos");
6    }
7    return parsed;
8  } catch (error) {
9    if (error instanceof SyntaxError) {
10      // Ce cas-là, on sait le gérer - les données sont corrompues
11      console.log("Données corrompues, j'utilise les valeurs par défaut");
12      return { species: "Unknown", status: "error" };
13    }
14    // Toute autre erreur - on la transmet plus haut
15    throw error;
16  }
17}

Cette fonction est exigeante, et c'est voulu. Un JSON corrompu donne un SyntaxError, dont elle sait se remettre en substituant un enregistrement de secours sûr. Une espèce manquante, c'est une tout autre histoire : les données ont été parsées correctement, donc deviner reviendrait à cacher sous le tapis une vraie panne dans la base du parc - et c'est pourquoi cette erreur remonte plus haut, vers du code qui a assez de contexte pour prendre une décision. try/catch doit filtrer les erreurs, et non les engloutir. Tout avaler dans un seul catch vide, c'est la recette de pannes invisibles pendant des mois.

Les classes d'erreurs personnalisées

À Jurassic Park, différentes situations exigent des procédures d'urgence spécialisées. En JavaScript, nous les obtenons en écrivant nos propres classes d'erreurs, qui étendent la classe intégrée Error.

1class DinosaurEscapeError extends Error {
2  constructor(species, enclosure) {
3    super("ALARME : " + species + " s'est échappé de l'enclos " + enclosure + " !");
4    this.name = "DinosaurEscapeError";
5    this.species = species;
6    this.enclosure = enclosure;
7  }
8}
9
10class FeedingError extends Error {
11  constructor(dinoName, reason) {
12    super("Erreur de nourrissage de " + dinoName + " : " + reason);
13    this.name = "FeedingError";
14    this.dinoName = dinoName;
15  }
16}
17
18class EnclosureVoltageError extends Error {
19  constructor(enclosure, voltage) {
20    super("Tension dans l'enclos " + enclosure + " : " + voltage + "V (minimum 5000V)");
21    this.name = "EnclosureVoltageError";
22    this.enclosure = enclosure;
23    this.voltage = voltage;
24  }
25}

La recette est chaque fois la même : le constructeur passe le message tout prêt à super(...), définit un nom lisible dans name, puis enregistre les informations supplémentaires dont l'équipe d'intervention a besoin - l'espèce, le numéro de l'enclos, la tension mesurée. Avec la même méthode, tu ajouteras un SensorError pour un capteur qui a cessé de répondre. Rien ne t'empêche non plus de placer au-dessus d'elles une classe parente commune, par exemple ParkError extends Error avec un champ code - une seule vérification instanceof ParkError attrapera alors toute la famille d'erreurs du parc d'un coup.

Une fois les classes prêtes, la ronde de sécurité peut réagir différemment à chaque menace :

1// Utilisation avec un catch sélectif
2function parkSecurityCheck(enclosures) {
3  for (const enc of enclosures) {
4    try {
5      if (enc.voltage < 5000) {
6        throw new EnclosureVoltageError(enc.name, enc.voltage);
7      }
8      if (enc.dinoEscaped) {
9        throw new DinosaurEscapeError(enc.species, enc.name);
10      }
11      console.log("Enclos " + enc.name + " - statut OK");
12    } catch (error) {
13      if (error instanceof DinosaurEscapeError) {
14        console.log("ALERTE ROUGE : " + error.message);
15        console.log("Espèce : " + error.species);
16      } else if (error instanceof EnclosureVoltageError) {
17        console.log("ALERTE JAUNE : " + error.message);
18      } else {
19        throw error; // Erreur inconnue - re-throw
20      }
21    }
22  }
23}

Lis le bloc catch comme le poste d'un répartiteur : une évasion reçoit une alerte rouge avec l'espèce lue directement dans l'objet d'erreur, une clôture trop faible une alerte jaune, et tout ce que le répartiteur ne reconnaît pas remonte au lieu d'être traité à l'aveugle. Comme try se trouve à l'intérieur de la boucle, un enclos défaillant n'interrompt pas le contrôle des autres - le scanner continue. Ce sont justement les classes d'erreurs personnalisées qui permettent un tel traitement précis : des menaces différentes méritent des procédures différentes, exactement comme dans un vrai parc, où des alarmes différentes déclenchent des protocoles différents.

Le chaînage optionnel (?.) et la coalescence des nuls (??)

Ces deux opérateurs sont les capteurs intelligents du parc. Ils permettent de circuler en toute sécurité dans des données parfois incomplètes, au lieu de faire planter le centre de contrôle au premier champ manquant.

Le chaînage optionnel (?.)

L'opérateur ?. accède aux propriétés imbriquées sans risque. Si une valeur quelconque sur le chemin s'avère être null ou undefined, toute l'expression s'arrête à cet endroit et renvoie un résultat, au lieu de faire planter le programme.

1const dinoRecord = {
2  species: "Velociraptor",
3  tracker: {
4    lastLocation: "Secteur C",
5    gps: { lat: 23.5, lng: -80.2 }
6  }
7};
8
9// Sans chaînage optionnel - risque de TypeError
10// const lat = dinoRecord.medical.lastCheckup.date; // TypeError !
11
12// Avec le chaînage optionnel - accès sécurisé
13const lat = dinoRecord.medical?.lastCheckup?.date; // undefined (sans erreur !)
14const gpsLat = dinoRecord.tracker?.gps?.lat;       // 23.5
15
16// Fonctionne aussi avec les méthodes et les index
17const dinoCall = dinoRecord.sounds?.roar?.();      // undefined (la méthode n'existe pas)
18const firstTag = dinoRecord.tags?.[0];             // undefined (pas de tableau)

Arrête-toi sur la ligne dinoRecord.medical?.lastCheckup?.date. Notre enregistrement n'a pas de propriété medical, et pourtant l'expression renverra undefined sans lever d'erreur : la chaîne se rompt au premier maillon manquant. Tu n'obtiendras pas null, tu n'obtiendras pas de chaîne vide "" et tu ne verras pas de TypeError, bien que medical n'existe effectivement pas - c'est précisément de cette exception que te protège la ligne commentée plus haut. Retiens aussi les deux variantes de l'opérateur : ?.() n'appellera la méthode que si elle existe, et ?.[0] n'accédera à l'index que si le tableau est vraiment là. Garde toutefois à l'esprit que ?. masque l'absence de données, mais ne la répare pas - un capteur muet reste muet.

La coalescence des nuls (??)

L'opérateur ?? substitue une valeur par défaut quand la partie gauche vaut null ou undefined. Contrairement à ||, il ne réagit ni à 0, ni à "", ni à false - dans les relevés du parc, ce sont des valeurs parfaitement valides.

1// Le problème avec || - il traite 0 et "" comme des valeurs vides
2const dinoWeight = 0;
3console.log(dinoWeight || "Aucune donnée");  // "Aucune donnée" (faux ! 0 est un poids valide)
4console.log(dinoWeight ?? "Aucune donnée");  // 0 (correct !)
5
6// Exemple pratique avec des données de dinosaures
7function getDinoInfo(dino) {
8  const name = dino?.name ?? "Dinosaure inconnu";
9  const weight = dino?.weight ?? "aucune donnée";
10  const isHealthy = dino?.isHealthy ?? false;
11  const speed = dino?.speed ?? 0;
12
13  return name + " | Poids [kg] : " + weight + " | En bonne santé : " + isHealthy + " | Vitesse : " + speed;
14}
15
16console.log(getDinoInfo({ name: "Rex", weight: 8000 }));
17console.log(getDinoInfo(null)); // Sans danger - ne lèvera pas d'erreur

La première paire de lignes résume toute l'affaire : un bébé dinosaure fraîchement éclos peut vraiment avoir un poids mesuré de 0, alors que || jetterait cette mesure à la poubelle et afficherait "Aucune donnée". Regarde aussi l'appel getDinoInfo(null) - il ne plante pas, car dino?.name court-circuite l'évaluation avant même la lecture de la propriété. Ensemble, ?. et ?? transforment une panne en un rapport de remplacement sensé.

La même protection compte pour la configuration des capteurs, où une valeur volontairement désactivée est une information à part entière, et non un trou à combler.

1const sensorConfig = {
2  voltage: 0,      // zéro volontaire
3  active: false,   // désactivé volontairement
4  label: ""        // nom volontairement vide
5};
6
7console.log(sensorConfig.voltage ?? 10000);     // 0 (le zéro explicite reste)
8console.log(sensorConfig.active ?? true);       // false (le false explicite reste)
9console.log(sensorConfig.label ?? "sans nom");  // "" (la chaîne vide explicite reste)
10console.log(sensorConfig.missing ?? "N/A");     // "N/A" (undefined, donc la valeur par défaut s'applique)

Seule la dernière ligne recourt à la valeur par défaut, car seule la dernière propriété manque vraiment. Le capteur désactivé reste désactivé, la clôture privée de courant le temps d'une inspection reste à zéro, et la sonde sans nom garde son étiquette vide. C'est exactement ce que tu attends d'un centre de contrôle qui n'a pas le droit d'inventer des relevés - si ces quatre lignes utilisaient ||, l'opérateur de garde verrait une tension de 10000V sur une clôture que l'on peut franchir tranquillement.

La combinaison de ?. et ??

En pratique, les deux opérateurs vont de pair : ?. mène à la valeur en toute sécurité, et ?? décide quoi afficher quand cette valeur n'arrive jamais.

1const parkData = {
2  enclosures: {
3    "A-1": { species: "T-Rex", voltage: 10000 }
4  }
5};
6
7// Lecture sécurisée des données avec une valeur par défaut
8const voltage = parkData.enclosures?.["B-2"]?.voltage ?? "Aucun capteur";
9console.log("Tension B-2 :", voltage); // "Aucun capteur"
10
11const rexVoltage = parkData.enclosures?.["A-1"]?.voltage ?? 0;
12console.log("Tension A-1 :", rexVoltage); // 10000

L'enclos B-2 n'a pas encore d'entrée dans les données, donc la chaîne donne undefined, et ?? substitue le message "Aucun capteur" - le tableau de l'opérateur de garde signale une lacune dans la couverture au lieu de se figer. L'enclos A-1 répond avec un vrai relevé, et puisque 10000 n'est ni null ni undefined, la valeur par défaut n'entre pas du tout en jeu. Une seule ligne remplace ici toute une cascade d'instructions if, et la surveillance ne s'interrompt pas à cause d'un seul enregistrement manquant.

La stratégie de gestion des erreurs

Une bonne stratégie de gestion des erreurs dans une application fonctionne comme le système de sécurité à plusieurs niveaux de Jurassic Park :

  1. Valide les données d'entrée - vérifie-les AVANT qu'elles ne deviennent un problème (comme le contrôle au portail du parc)
  2. Utilise des types d'erreurs précis - crée tes propres classes pour les différentes situations (une alarme différente pour chaque menace)
  3. Gère les erreurs au bon niveau - attrape-les là où tu sais réellement les réparer (on ne signale pas une mangeoire coincée au directeur du parc)
  4. Transmets plus haut les erreurs inconnues - si tu ne sais pas comment gérer une erreur, renvoie-la vers le haut de la chaîne
  5. Libère les ressources dans finally - ferme les connexions, les fichiers et les clôtures, quoi qu'il arrive

"Une alarme déclenchée n'est pas une catastrophe, c'est une information", répète le Dr Rex en refermant le panneau de commande. Tiens-t'en à ces cinq règles et retiens l'essentiel : try/catch/finally est le protocole d'urgence du parc, les classes d'erreurs personnalisées sont des alarmes précises qui indiquent à l'équipe quelle procédure lancer, et ?. et ?? sont les capteurs grâce auxquels tu ne déclenches pas l'alarme sans raison.

Code de cette leçon : index.js
1// Gestion des erreurs en JavaScript - Édition Parc Jurassique
2console.log("=== SYSTÈME DE SÉCURITÉ DU PARC JURASSIQUE ===\n");
3
4// ================================================
5// 1. TRY/CATCH - protocole d'urgence
6// ================================================
7console.log("--- 1. try/catch ---");
8
9try {
10  const dinoData = JSON.parse('{ "species": "T-Rex", "weight": 8500 }');
11  console.log("Données du dinosaure:", dinoData.species, "-", dinoData.weight + "kg");
12} catch (error) {
13  console.log("ALARME ! Erreur de parsing des données:", error.message);
14}
15
16// Erreur de parsing - catch attrape l'erreur
17try {
18  const brokenData = JSON.parse("{ mauvais format }");
19} catch (error) {
20  console.log("\nErreur attrapée:", error.message);
21  console.log("Type d'erreur:", error.constructor.name);
22}
23
24// ================================================
25// 2. FINALLY - s'exécute toujours
26// ================================================
27console.log("\n--- 2. try/catch/finally ---");
28
29function feedDinosaur(dinoName, food) {
30  console.log("\nOuverture de la mangeoire pour " + dinoName + "...");
31  try {
32    if (!food) {
33      throw new Error("Aucune nourriture dans le système !");
34    }
35    if (food === "herbe" && dinoName === "Rex") {
36      throw new TypeError("Le T-Rex n'est pas herbivore !");
37    }
38    console.log("Distribution de " + food + " pour " + dinoName + " - succès !");
39  } catch (error) {
40    console.log("ALARME: " + error.message);
41  } finally {
42    console.log("Fermeture de la mangeoire - sécurité !");
43  }
44}
45
46feedDinosaur("Rex", "viande");
47feedDinosaur("Rex", null);
48feedDinosaur("Rex", "herbe");
49
50// ================================================
51// 3. TYPES D'ERREURS
52// ================================================
53console.log("\n--- 3. Types d'erreurs ---");
54
55// TypeError
56try {
57  const dino = null;
58  dino.roar();
59} catch (e) {
60  console.log("TypeError:", e.message);
61}
62
63// RangeError
64try {
65  const arr = new Array(-1);
66} catch (e) {
67  console.log("RangeError:", e.message);
68}
69
70// ReferenceError
71try {
72  console.log(dinosaureInexistant);
73} catch (e) {
74  console.log("ReferenceError:", e.message);
75}
76
77// ================================================
78// 4. CLASSES D'ERREURS PERSONNALISÉES
79// ================================================
80console.log("\n--- 4. Classes d'erreurs personnalisées ---");
81
82class DinosaurEscapeError extends Error {
83  constructor(species, enclosure) {
84    super("ALARME: " + species + " s'est échappé de l'enclos " + enclosure + " !");
85    this.name = "DinosaurEscapeError";
86    this.species = species;
87    this.enclosure = enclosure;
88  }
89}
90
91class EnclosureVoltageError extends Error {
92  constructor(enclosure, voltage) {
93    super("Tension dans " + enclosure + ": " + voltage + "V (min 5000V)");
94    this.name = "EnclosureVoltageError";
95    this.enclosure = enclosure;
96    this.voltage = voltage;
97  }
98}
99
100const enclosures = [
101  { name: "A-1", species: "T-Rex", voltage: 10000, dinoEscaped: false },
102  { name: "B-3", species: "Raptor", voltage: 3000, dinoEscaped: false },
103  { name: "C-2", species: "Dilophosaurus", voltage: 8000, dinoEscaped: true },
104];
105
106for (const enc of enclosures) {
107  try {
108    if (enc.dinoEscaped) {
109      throw new DinosaurEscapeError(enc.species, enc.name);
110    }
111    if (enc.voltage < 5000) {
112      throw new EnclosureVoltageError(enc.name, enc.voltage);
113    }
114    console.log("Enclos " + enc.name + " - OK");
115  } catch (error) {
116    if (error instanceof DinosaurEscapeError) {
117      console.log("ALERTE ROUGE: " + error.message);
118    } else if (error instanceof EnclosureVoltageError) {
119      console.log("ALERTE JAUNE: " + error.message);
120    }
121  }
122}
123
124// ================================================
125// 5. OPTIONAL CHAINING et NULLISH COALESCING
126// ================================================
127console.log("\n--- 5. ?. et ?? ---");
128
129const dinoRecord = {
130  species: "Velociraptor",
131  tracker: { lastLocation: "Secteur C", gps: { lat: 23.5 } }
132};
133
134// Optional chaining - accès sécurisé
135console.log("GPS:", dinoRecord.tracker?.gps?.lat);        // 23.5
136console.log("Médical:", dinoRecord.medical?.lastCheckup);  // undefined
137
138// Nullish coalescing - valeur par défaut
139const weight = 0;
140console.log("|| :", weight || "Aucune donnée");   // "Aucune donnée" (mauvais !)
141console.log("??:", weight ?? "Aucune donnée");    // 0 (correct !)
142
143// Combinaison
144const voltage = dinoRecord.tracker?.voltage ?? "Aucun capteur";
145console.log("Tension:", voltage);

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. Quelle est la bonne façon de gérer une erreur pouvant survenir lors de l'analyse (parsing) JSON en JavaScript ?

  2. 2. Quand le bloc finally s'exécute-t-il dans une construction try/catch/finally ?

  3. 3. Que va retourner l'expression dinoRecord.medical?.lastCheckup?.date si l'objet dinoRecord n'a pas de propriété medical ?

Tâches pratiques dans le jeu

  • Éditeur de code

    Écris une fonction findDinosaur(dinosaurs, id) qui renvoie le dinosaure ayant l'id donné et, s'il n'existe pas, lève new Error("Aucun dinosaure avec l'ID " + id). Écris une fonction feedDinosaur(dinosaurs, id, food) : dans le bloc try, trouve le dinosaure avec findDinosaur, lève une TypeError quand la nourriture ne correspond pas au régime (carnivore mange 'meat', herbivore mange 'plants'), sinon renvoie true ; dans catch, affiche error.message et renvoie false ; dans finally, affiche 'Fermeture de la mangeoire'. Écris aussi readVoltage(record), qui lit record.sensors.voltage en toute sécurité avec l'opérateur ?. et renvoie 'Pas de capteur' quand la valeur manque (utilise ??).

  • Tri vertical

    Range les étapes de la gestion des erreurs en JavaScript à partir du moment où le problème survient :

  • Clique dans l'ordre

    Range les éléments dans le bon ordre pour construire un bloc try/catch en JavaScript :

  • Éditeur de code

    Crée les classes DinosaurEscapeError(species, enclosure), FeedingError(dinoName, reason) et SensorError(sensorType, enclosure), qui étendent Error, transmettent un message à super(), définissent this.name avec le nom de la classe et enregistrent les paramètres dans des propriétés du même nom. Écris une fonction securityScan(enclosures) qui, pour chaque enclos { name, species, voltage, dinoEscaped }, lève dans un bloc try une DinosaurEscapeError quand dinoEscaped vaut true, ou une SensorError('voltage', name) quand voltage < 5000. Dans catch, reconnais l'erreur avec instanceof et affiche 'ALERTE ROUGE : ' + message ou 'ALERTE JAUNE : ' + message, et relance toute erreur d'un autre type. La fonction renvoie un objet avec le nombre d'alertes { red, yellow }.

Articles utiles