TypeScript utilise les try, catch, finally et throw de JavaScript. Ce qui est propre à TypeScript, c'est la variable du catch : avec strict activé, elle a le type unknown, donc vous vérifiez ce qui a été levé avant de l'utiliser.
Le côté exécution (comment la pile se déroule, quels types d'erreur intégrés existent) est présenté dans try/catch en JavaScript.
Pourquoi la variable du catch est unknown
JavaScript peut lever n'importe quoi : une Error, une chaîne, un nombre, undefined, un objet venant d'une bibliothèque. TypeScript ne peut pas savoir lequel, donc sous strict (l'option useUnknownInCatchVariables) la variable est unknown et vous devez restreindre son type avant de l'utiliser :
index.ts(5,34): error TS18046: 'e' is of type 'unknown'.
Sans strict, e est any et le même code compile, puis se passe mal à l'exécution quand une chaîne est levée : e.message vaut undefined, et tout accès plus profond, comme e.message.length, lève une TypeError. Une annotation ne vous tire pas d'affaire non plus : catch (e: Error) provoque l'erreur TS1196 (Catch clause variable type annotation must be 'any' or 'unknown' if specified).
Restreindre le type de l'erreur
instanceof Error couvre tout ce qui est construit sur Error, y compris TypeError, SyntaxError, RangeError et vos propres sous-classes. Pour le reste, convertissez la valeur en chaîne. Une petite fonction utilitaire garde les appels courts :
Un catch qui ne gère qu'une partie des erreurs doit relancer les autres : if (!(e instanceof ValidationError)) throw e;. Avaler les erreurs inconnues cache de vrais bugs.
Lever des erreurs
throw accepte n'importe quelle expression, et TypeScript ne le restreint pas. Levez tout de même des objets Error : ils portent une trace de pile, et chaque test instanceof Error du code en dépend.
Une fonction qui lève toujours une exception a le type de retour never, que TypeScript utilise pour restreindre les types après l'appel :
Depuis ES2022, Error accepte un second argument avec une cause, qui relie une erreur de bas niveau à celle que vous levez : throw new Error("could not load settings", { cause: e }). L'appelant peut lire err.cause (typé unknown).
Classes d'erreur personnalisées
Une sous-classe d'Error permet aux appelants de distinguer les échecs avec instanceof et de transporter des données supplémentaires. Définissez name, car celui hérité vaut "Error" et il apparaît dans les logs et dans String(err) :
Testez d'abord la classe la plus spécifique, puisqu'une NotFoundError est aussi une HttpError. Le modificateur override est facultatif ici, sauf si noImplicitOverride est activé.
Les anciens guides ajoutent Object.setPrototypeOf(this, new.target.prototype) dans chaque constructeur d'erreur. C'était nécessaire quand les classes étaient compilées en fonctions ES5, où instanceof ne fonctionnait plus pour les sous-classes d'Error. TypeScript 7 ne prend plus en charge target: "es5" (il signale TS5108: Option 'target=ES5' has been removed), et avec ES2015 ou plus le class ... extends Error natif fonctionne, comme ci-dessus.
Le pattern Result
TypeScript ne suit pas les erreurs qu'une fonction peut lever, donc rien ne rappelle aux appelants de les gérer. Pour les échecs prévus (une saisie invalide, un enregistrement absent), renvoyer une valeur qui dit « succès ou échec » fait entrer l'erreur dans le système de types :
Lire r.value avant de tester r.ok est une erreur de compilation, et c'est tout l'intérêt. Utilisez les exceptions pour l'imprévu (bugs, connexion coupée) et les valeurs Result pour les issues sur lesquelles l'appelant doit décider.
Les erreurs dans le code asynchrone
Une promesse rejetée devient une erreur levée au niveau du await, donc un try/catch autour de await fonctionne de la même façon, et la variable est là aussi unknown. La seule différence concerne .catch() sur une promesse : le paramètre de son callback est typé any, pas unknown, donc annotez-le vous-même (.catch((e: unknown) => ...)). Voir async/await pour des exemples.
Erreurs courantes
- Lire
e.messagesans restreindre le type. Sousstrict, cela ne compile pas ; sansstrict, cela casse quand ce qui est levé n'est pas uneError. - Tout capturer et continuer. Gérez les erreurs que vous attendez et relancez les autres.
- Oublier
namesur les erreurs personnalisées. Les logs affichent alorsErrorpour toutes. - Lever des chaînes.
throw "failed"n'a pas de trace de pile et échoue aux testsinstanceof Error.
Questions fréquentes
Quel est le type de l'erreur dans un bloc catch en TypeScript ?
unknown quand strict est activé (l'option useUnknownInCatchVariables), sinon any. JavaScript peut lever n'importe quelle valeur, pas seulement des objets Error, donc TypeScript vous oblige à vérifier. Restreignez le type avec if (e instanceof Error) avant de lire e.message.
Peut-on typer la variable du catch en Error en TypeScript ?
Non. catch (e: Error) provoque l'erreur TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. Seuls unknown et any sont autorisés, car rien ne garantit ce qui a été levé. Restreignez plutôt le type dans le bloc.
Comment lever une erreur en TypeScript ?
Comme en JavaScript : throw new Error("message"), ou une instance d'une sous-classe intégrée ou personnalisée comme new RangeError(...). TypeScript vous laisse lever n'importe quelle valeur, mais lever des objets Error conserve une trace de pile et fait fonctionner les tests instanceof Error.
Comment créer une classe d'erreur personnalisée en TypeScript ?
Étendez Error, appelez super(message, options) et définissez name : class NotFoundError extends Error { name = "NotFoundError"; }. Les champs supplémentaires vont dans le constructeur. Avec toute cible prise en charge (ES2015 et plus), instanceof NotFoundError fonctionne sans correctif de prototype.
TypeScript a-t-il des exceptions vérifiées ou une clause throws ?
Non. Le type d'une fonction ne dit rien de ce qu'elle peut lever, et le compilateur ne vérifie jamais que les erreurs sont gérées. Pour les erreurs que l'appelant doit traiter, renvoyez-les comme des valeurs avec un type Result (une union discriminée de succès et d'échec), pour que le système de types les vérifie.