Question 1Quel est le name d'une exception créée à partir de class ShippingError extends Error {} ?
Lever des exceptions — throw et erreurs personnalisées
Lève une exception avec throw new Error, crée tes propres types avec extends Error et garde l'erreur d'origine avec cause.
Même si la page de commande envoie une quantité de 0, la fonction qui calcule le sous-total fait son calcul sans broncher, et une commande d'un montant nul passe. Afficher un avertissement n'arrêtera pas non plus l'appelant : il ne remarque pas le problème et enregistre la commande.
Cet article couvre throw new Error, qui te permet de lever toi-même une exception, et les erreurs personnalisées, des types d'erreur que tu définis toi-même.
S'arrêter sur une valeur invalide — throw new Error
Il faut interrompre le calcul du sous-total quand la quantité est un nombre décimal ou négatif. Le signaler avec return null ne suffit pas : si l'appelant oublie de vérifier, ajouter les frais de port avec null + 500 donne 500, et le calcul continue.
Un objet Error (une valeur dotée de name et de message, conçue pour être levée comme exception) se crée avec new Error("message"). Si tu le passes à throw, une exception est levée sur-le-champ ; si l'appel a eu lieu dans try, cette valeur arrive dans l'error de catch.
function calcSubtotal(unitPrice, quantity) {
// Si ce n'est pas un entier supérieur ou égal à 1, lever une exception tout de suite
if (!Number.isInteger(quantity) || quantity < 1) {
// new Error seul n'arrête rien. Il faut le passer à throw pour en faire une exception
throw new Error(`La quantité doit être un entier supérieur ou égal à 1 : ${quantity}`);
}
return unitPrice * quantity;
}
try {
console.log(calcSubtotal(1200, 3)); // 3600
console.log(calcSubtotal(1200, 0)); // lève une exception, donc rien ne s'affiche
console.log("Commande confirmée"); // cette ligne ne s'exécute pas non plus
} catch (error) {
console.log(error.name); // Error
console.log(error.message); // La quantité doit être un entier supérieur ou égal à 1 : 0
}
Place le if de validation en haut de la fonction, avant le calcul. Comme return, throw quitte la fonction sur-le-champ : le return unitPrice * quantity; qui suit ne s'exécute pas, et l'appelant ne reçoit jamais 0.
Créer un type pour les erreurs de saisie — extends Error
Une exception levée avec new Error a toujours Error pour name, quelle que soit la vérification concernée. Un code postal avec le mauvais nombre de chiffres et un article en rupture de stock arrivent sous le même nom : le bloc catch ne peut donc pas savoir s'il s'agit d'un échec que l'utilisateur peut régler en corrigeant sa saisie.
Ajouter extends Error à une classe (la syntaxe qui définit un type d'objet que tu crées avec new) crée un type qui hérite du comportement d'Error. Si tu écris name = "ValidationError"; entre les accolades, les exceptions créées à partir de ce type auront cette chaîne comme name.
// Hériter d'Error pour créer un type dédié aux erreurs de saisie
class ValidationError extends Error {
name = "ValidationError"; // devient le name des exceptions créées avec new
}
function checkZipCode(zipCode) {
if (zipCode.length !== 5) {
throw new ValidationError(`Le code postal doit comporter 5 chiffres : ${zipCode}`);
}
return zipCode;
}
try {
checkZipCode("7500");
} catch (error) {
console.log(error.name); // ValidationError
console.log(error.message); // Le code postal doit comporter 5 chiffres : 7500
console.log(error instanceof Error); // true
}
- Pour une exception créée avec
new Error("message")comme pour tous les types de ce cadre,instanceof Errorvauttrue
SyntaxError/TypeError/RangeError— les types vus dans l'article précédent
ValidationError— représente une erreur de saisienamevaut le"ValidationError"défini dans la classe- instanceof ValidationError ne vaut true que dans ce cadre
Tu peux aussi passer une chaîne à throw, mais une chaîne n'a ni name ni message : lire error.message dans catch donne alors undefined. Le tableau ci-dessous montre ce que tu peux lire dans catch selon la valeur passée à throw.
| Valeur passée à throw | name lisible dans catch | instanceof Error |
|---|---|---|
| "Code postal invalide" (une chaîne) | undefined (une chaîne n'en a pas) | false |
| new Error("Code postal invalide") | Error | true |
| ValidationError avec name défini | ValidationError | true |
| ValidationError sans name défini | Error (hérité tel quel) | true |
Vérifier d'abord tes propres types — l'ordre des instanceof
Une erreur de saisie, l'utilisateur peut la corriger ; mais une exception inattendue comme une TypeError ne disparaîtra pas, quoi qu'il modifie dans sa saisie. Tu veux que catch affiche deux messages différents, mais si tu écris les vérifications dans le mauvais ordre, même les erreurs de saisie reçoivent le message prévu pour les exceptions inattendues.
if et else if sont évalués de haut en bas, et seule la première branche qui correspond s'exécute. Une ValidationError donne aussi true pour instanceof Error : si instanceof Error vient en premier, une ValidationError atterrit elle aussi dans cette branche.
class ValidationError extends Error {
name = "ValidationError";
}
const error = new ValidationError("Le code postal doit comporter 5 chiffres");
// Si Error est vérifié en premier, une ValidationError atterrit aussi dans cette branche
if (error instanceof Error) {
console.log("Enregistrement impossible"); // Enregistrement impossible
} else if (error instanceof ValidationError) {
console.log("Vérifiez votre saisie"); // on n'arrive pas ici
}
// Vérifier d'abord la sous-classe
if (error instanceof ValidationError) {
console.log("Vérifiez votre saisie"); // Vérifiez votre saisie
} else if (error instanceof Error) {
console.log("Enregistrement impossible");
}
Les types intégrés comme TypeError héritent eux aussi d'Error : la branche instanceof Error placée en dernier les intercepte donc tous. Sers-toi de cette branche pour afficher un message différent pour les exceptions qu'une correction de la saisie ne réglera pas.
Joindre l'exception d'origine — cause et exceptions non interceptées
Prenons le cas d'une SyntaxError levée dans une fonction qui lit la réponse d'un service de paiement. Si tu te contentes de la relancer, son type n'indique pas à l'appelant que l'échec s'est produit pendant le traitement d'un paiement. Mais si tu la remplaces par une exception d'un nouveau type, tu perds l'information qu'il s'agissait au départ d'une SyntaxError.
cause (une option qui joint l'exception d'origine à une nouvelle exception) se place dans le deuxième argument, comme dans new Error("message", { cause: error }). Cela fonctionne de la même façon avec les types créés avec extends Error, et le code qui intercepte la nouvelle exception peut lire l'exception d'origine dans error.cause.
class PaymentError extends Error {
name = "PaymentError";
}
function readPayment(text) {
try {
return JSON.parse(text);
} catch (error) {
// Joindre l'exception d'origine comme cause et la lever comme erreur de paiement
throw new PaymentError("Impossible de lire le résultat du paiement", { cause: error });
}
}
try {
readPayment('{"orderId":"P-3107","amount":}');
} catch (error) {
console.log(error.name); // PaymentError
console.log(error.message); // Impossible de lire le résultat du paiement
console.log(error.cause.name); // SyntaxError
}
error.namevautPaymentErrorerror.messagevautImpossible de lire le résultat du paiement
error.cause.namevautSyntaxErrorerror.cause.messageest la description fournie par JSON.parse, inchangée
Si tu remplaces l'exception sans cause, error.cause vaut undefined, et tu ne peux plus lire ni le type ni la description d'origine. Avec cause, l'appelant peut se servir de error.name pour afficher un message d'échec de paiement, tout en consignant le contenu de error.cause.
Une exception que personne n'intercepte arrête l'exécution
Si aucun catch ne reçoit une exception levée, elle devient une exception non interceptée (une exception qu'aucun catch ne traite), et l'exécution s'arrête sur-le-champ ; aucune des lignes restantes ne s'exécute. Si tu retires le try externe du code ci-dessus, une erreur s'affiche avec le nom du type et le message, comme PaymentError: Impossible de lire le résultat du paiement.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Si tu vérifies instanceof Error en premier, dans quelle branche va une exception ValidationError ?
Question 3Tu as intercepté avec catch (error) une exception levée avec cause jointe. Comment lis-tu le nom de l'exception d'origine ?