Back

Comment créer des erreurs personnalisées en JavaScript

Comment créer des erreurs personnalisées en JavaScript

Attraper une erreur est simple. Savoir quelle erreur vous avez attrapée — et pourquoi — c’est là que les choses se compliquent. Lorsque votre bloc catch gère un timeout de base de données de la même manière qu’une saisie utilisateur invalide, le débogage devient une devinette.

Les erreurs JavaScript personnalisées résolvent ce problème. Elles vous permettent de créer des types d’erreurs structurés et identifiables qui transportent un contexte significatif à travers votre application. Ce guide couvre l’approche moderne utilisant les fonctionnalités ES2022+, incluant Error.cause pour le chaînage d’erreurs et les classes d’erreurs JavaScript qui fonctionnent de manière fiable sur les navigateurs et environnements d’exécution actuels.

Points clés à retenir

  • Étendez la classe native Error en utilisant la syntaxe class ... extends Error pour des erreurs personnalisées propres et maintenables
  • Passez à la fois message et options à super() pour préserver les traces de pile et activer le chaînage d’erreurs
  • Utilisez Error.cause (ES2022+) pour encapsuler et préserver les erreurs originales provenant de sources externes
  • Ajoutez des champs structurés comme statusCode ou field au lieu d’encoder le contexte dans les chaînes de message
  • Gardez les hiérarchies d’erreurs peu profondes — une erreur de base avec quelques sous-types spécialisés couvre la plupart des besoins

Le pattern standard : étendre Error

La gestion d’erreurs moderne en JavaScript commence avec la syntaxe de classe. Oubliez la manipulation de prototype héritée — class ... extends Error est l’approche de référence :

class ValidationError extends Error {
  constructor(message, options) {
    super(message, options)
    this.name = 'ValidationError'
  }
}

L’appel super(message, options) est crucial. Il transmet à la fois le message et un objet d’options au constructeur parent Error, qui gère automatiquement la capture de la trace de pile.

Définir this.name garantit que votre erreur s’identifie correctement dans les traces de pile et les journaux. Sans cela, votre erreur personnalisée s’affiche comme une “Error” générique.

Ajouter des champs structurés

Les chaînes de message simples sont fragiles. Parser “Invalid email: user@” pour extraire le contexte est sujet aux erreurs. À la place, ajoutez des champs structurés :

class HttpError extends Error {
  constructor(message, statusCode, options) {
    super(message, options)
    this.name = 'HttpError'
    this.statusCode = statusCode
  }
}

throw new HttpError('Resource not found', 404)

Maintenant, votre gestionnaire d’erreurs peut vérifier error.statusCode directement plutôt que de parser des chaînes. Ce pattern s’adapte proprement — ajoutez details, code, ou tout champ spécifique au domaine dont votre application a besoin.

Chaînage d’erreurs avec Error.cause

Lors de l’encapsulation d’erreurs provenant de sources externes — bases de données, API, bibliothèques — vous devez préserver l’erreur originale. L’option cause, introduite dans ES2022, gère cela :

async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`)
    if (!response.ok) {
      throw new HttpError('User fetch failed', response.status)
    }
    return response.json()
  } catch (error) {
    throw new HttpError('Unable to load user', 500, { cause: error })
  }
}

L’erreur originale reste accessible via error.cause, préservant la chaîne d’erreurs complète pour le débogage. Ceci est supporté par les navigateurs modernes et Node.js — aucun polyfill nécessaire.

Construire des hiérarchies d’erreurs simples

Les hiérarchies d’erreurs JavaScript fonctionnent mieux lorsqu’elles restent peu profondes. Une erreur d’application de base avec quelques sous-types spécialisés couvre la plupart des besoins :

class AppError extends Error {
  constructor(message, options = {}) {
    super(message, { cause: options.cause })
    this.name = this.constructor.name
    this.statusCode = options.statusCode ?? 500
  }
}

class NotFoundError extends AppError {
  constructor(message, options = {}) {
    super(message, { ...options, statusCode: 404 })
  }
}

class ValidationError extends AppError {
  constructor(message, field, options = {}) {
    super(message, { ...options, statusCode: 400 })
    this.field = field
  }
}

L’utilisation de this.constructor.name définit automatiquement le nom de l’erreur à partir de la classe, réduisant le code répétitif dans les sous-classes.

Discriminer les erreurs dans les flux asynchrones

Les erreurs personnalisées brillent dans le code asynchrone où vous devez gérer différents modes d’échec :

try {
  const user = await fetchUser(userId)
} catch (error) {
  if (error instanceof NotFoundError) {
    return { status: 404, body: { message: 'User not found' } }
  }
  if (error instanceof ValidationError) {
    return { status: 400, body: { message: error.message, field: error.field } }
  }
  // Erreur inattendue — journaliser et retourner une réponse générique
  console.error('Unexpected error:', error.cause ?? error)
  return { status: 500, body: { message: 'Internal error' } }
}

La vérification instanceof fonctionne correctement à travers la chaîne d’héritage. Vous pouvez également discriminer en vérifiant les champs personnalisés ou error.name lorsque vous traitez des erreurs sérialisées.

Pour les scénarios impliquant plusieurs échecs simultanés, AggregateError fournit un moyen standard de regrouper les erreurs ensemble — utile pour les opérations parallèles où plusieurs peuvent échouer indépendamment.

Considération TypeScript

Si vous utilisez TypeScript, définissez target: "ES2022" ou supérieur, ou incluez "ES2022" dans votre tableau lib dans tsconfig.json. Cela garantit un typage approprié pour la propriété cause dans l’interface ErrorOptions.

Conclusion

Les erreurs JavaScript personnalisées construites avec class ... extends Error vous donnent des exceptions structurées et identifiables qui transportent le contexte à travers votre application. Passez message et options à super(), utilisez cause pour le chaînage d’erreurs, et ajoutez des champs spécifiques au domaine au lieu d’encoder l’information dans les chaînes de message. Gardez les hiérarchies peu profondes, et votre gestion d’erreurs devient prévisible et débogable.

FAQ

Les erreurs personnalisées vous permettent de distinguer entre différents types d'échec de manière programmatique. Au lieu de parser les messages d'erreur pour déterminer ce qui s'est mal passé, vous pouvez utiliser des vérifications instanceof ou inspecter des propriétés personnalisées comme statusCode ou field. Cela rend la gestion d'erreurs plus fiable et votre code plus facile à déboguer et maintenir.

Error.cause a été introduit dans ES2022 et est supporté dans tous les navigateurs modernes et Node.js 16.9+. Pour les environnements plus anciens, l'option cause est simplement ignorée — votre code fonctionnera toujours, mais la propriété cause sera undefined. Envisagez un polyfill uniquement si vous devez supporter des environnements d'exécution hérités.

Gardez-la peu profonde — typiquement deux niveaux au maximum. Une seule classe d'erreur d'application de base avec une poignée de sous-types spécialisés couvre la plupart des cas d'usage. Les hiérarchies profondes ajoutent de la complexité sans bénéfice proportionnel et rendent le refactoring plus difficile à mesure que votre application évolue.

Oui. Lorsque plusieurs opérations peuvent échouer indépendamment, encapsulez les échecs individuels dans des erreurs personnalisées, puis regroupez-les en utilisant AggregateError. Chaque erreur dans le tableau errors conserve son type, vous pouvez donc toujours utiliser des vérifications instanceof lors du traitement des résultats.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay