# Vous utilisez mal les exceptions

> Les exceptions ne sont pas faites pour gérer les erreurs prévisibles. Découvrez le type Result, alternative au try/catch.

- Author: Mathieu Eveillard
- Published: 2025-12-17
- Canonical: https://www.mathieueveillard.com/fr/blog/result-monad/
- Translation (en): https://www.mathieueveillard.com/blog/result-monad.md

![Vous utilisez mal les exceptions](https://www.mathieueveillard.com/blog/2025-12-17-result-monad/result-monad.webp)

Vous pourriez être tentés d'écrire ceci :

```ts
const divide = (a: number, b: number): number => {
  if (b === 0) {
    throw Error("Division by 0.");
  }
  return a / b;
};
```

Bien que simple, cette fonction est très problématique. Des problèmes, j'en vois au moins 3 :

1. Cette fonction n'est pas « honnête ». En effet, rien dans sa signature `(a: number, b: number) => number` n'indique qu'elle peut lancer une exception, c'est-à-dire produire un _side effect_. Il faut lire l'implémentation pour le savoir… merci TypeScript 😉

2. D'autre part, si cette fonction n'est pas juste un pur utilitaire en dehors de tout contexte métier, on pourrait vouloir donner plus de sens aux paramètres `a` et `b`, et encapsuler les primitives. Si `b` n'est plus de type `number` mais `Denominator`, ce serait probablement à lui d'empêcher la création d'un dénominateur nul.

3. Mais, plus profondément, la gestion des erreurs pose un problème [sémantique](https://www.mathieueveillard.com/fr/blog/erreur-vs-exception). Le cas-limite de la division par 0 est totalement anticipable. Vous n'avez donc aucune raison, pour le traiter, de lancer une exception, mécanisme censé rester… exceptionnel. Oui, lancer une exception, c'est reconnaître que votre application est en PLS, qu'elle ne sait plus gérer.

Autrement dit, vous souhaitez communiquer auprès de l'appelant, possiblement l'utilisateur final, pour qu'il modifie son comportement. Tandis qu'avec une exception, c'est aux développeurs que vous parlez, pour leur dire qu'il y a un bug dans l'application et qu'ils doivent le corriger sans trop tarder s'il vous plaît !

Vous devez donc trouver autre chose pour gérer ce type d'erreurs.

Ce que nous voulons, c'est un type, appelons-le `Result`, qui nous permette de représenter aussi bien le cas nominal, où la division peut avoir lieu (cas `Ok`) que le cas-limite où la division ne peut être effectuée parce que `b` est nul (cas `Ko`).

De surcroît, nous souhaitons que ce type nous rende service en exposant une fonction `bind` qui permettra :

- Dans le cas passant (`Ok`), d'effectuer d'autres traitements à partir du résultat de la division ;
- Dans le cas non-passant (`Ko`), de faire suivre l'erreur, puisqu'il n'y a rien à faire de plus.

C'est ce que l'on appelle du **polymorphisme** : les implémentations `ok()` et `ko()` satisfont toutes deux le contrat exposé par le type `Result`, mais chacune à sa manière, puisque dans un cas on a une valeur, dans l'autre une erreur.

Saperlipopette, que cela est intéressant !

En effet, nous pouvons à présent chaîner les instructions, et écrire aussi bien `divide(1, 2).bind(square)` que `divide(1, 0).bind(square)`. Les erreurs éventuelles ne mettent plus le programme en échec, la fonction `divide: (a: number, b: number) => Result<number, DivisionByZeroError>` dit bien ce qu'elle fait, tout le monde est content.

Il ne nous reste plus qu'à ajouter une méthode `finally` pour produire des effets de bord en bout de chaîne, ce qui, dans un contrôleur HTTP, par exemple, se traduirait ainsi :

- `Ok` -> code 200
- `Ko` -> code 422 ([Unprocessable Content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/422))

« Quid des opérations asynchrones ? » me direz-vous. Ce cas requiert un peu plus de travail, mais l'idée reste simple : composer `Result` et `Promise` pour manipuler un type `Promise<Result>` et ne plus utiliser `Promise.reject`, puisque `Promise.resolve` manipule un type `Result` qui représente _déjà_ les cas passant et non-passant.

Vous pouvez introduire ces quelques lignes de code dans votre codebase, elles ont le mérite d'être simples et permettent à chacun de comprendre ce qu'il se passe concrètement. Ou bien vous pouvez faire appel à une bibliothèque de code, la référence dans l'univers TypeScript étant [Neverthrow](https://github.com/supermacro/neverthrow).

En tout cas bravo, car vous avez créé votre première Monade !
