Introduction : Pourquoi la gestion des erreurs en TypeScript ne se limite pas à try/catch
TypeScript ajoute un typage statique à JavaScript, mais les erreurs d'exécution (runtime errors) restent une partie de toute application. Contrairement au JS pur, où catch(e) retourne any, TypeScript propose des mécanismes plus stricts. Une bonne gestion des erreurs ne consiste pas seulement à prévenir les plantages, mais aussi à créer un système compréhensible pour le débogage et la maintenance du code. Dans cet article, nous allons aborder tous les aspects : du try/catch de base à la création de hiérarchies d'erreurs personnalisées avec un typage complet.
1. Syntaxe de base de try/catch et le problème du type unknown
Construction classique
La construction de base en TypeScript est la même qu'en JavaScript :
try { // Code qui peut lever une exception const data = JSON.parse('{"name": "TypeScript"}'); console.log(data.name);} catch (error) { // Gestion de l'erreur console.error('Une erreur est survenue :', error);}Cependant, il y a une différence importante : en TypeScript, la variable error dans le bloc catch a le type unknown (et non any, comme en JavaScript, sauf indication contraire). Cela est fait pour la sécurité des types.
Le problème avec unknown et comment le résoudre
Le type unknown signifie que nous ne pouvons pas simplement accéder aux propriétés de l'erreur, par exemple error.message. Le compilateur générera une erreur :
catch (error) { // Erreur TS2571 : Object is of type 'unknown'. console.log(error.message); // ❌ Ne compilera pas}Pour accéder aux propriétés, il faut réduire le type :
catch (error) { if (error instanceof Error) { console.log(error.message); // ✅ Fonctionne } else { console.log('Erreur inconnue :', error); }}Ou utiliser une vérification de la présence de la propriété :
catch (error) { if (error && typeof error === 'object' && 'message' in error) { console.log((error as { message: string }).message); }}2. Création de classes d'erreurs personnalisées avec typage
Pourquoi est-ce important ?
L'objet Error standard ne contient que name, message et stack. Dans les projets réels, des champs supplémentaires sont souvent nécessaires : code d'erreur, statut HTTP, données pour la journalisation. TypeScript permet de créer une hiérarchie de classes d'erreurs avec une prise en charge complète des types.
Exemple de classe personnalisée
// Classe de base pour toutes les erreurs de l'applicationclass AppError extends Error { public readonly statusCode: number; public readonly isOperational: boolean;
constructor(message: string, statusCode: number = 500, isOperational: boolean = true) { super(message); this.name = 'AppError'; this.statusCode = statusCode; this.isOperational = isOperational; // Restauration du prototype (nécessaire pour TypeScript) Object.setPrototypeOf(this, new.target.prototype); }}
// Erreurs spécialiséesclass NotFoundError extends AppError { constructor(resource: string) { super(`${resource} non trouvé`, 404); this.name = 'NotFoundError'; }}
class ValidationError extends AppError { public readonly errors: string[];
constructor(errors: string[]) { super('Erreur de validation', 400); this.name = 'ValidationError'; this.errors = errors; }}Utilisation avec try/catch
function getUser(id: number): User { if (id <= 0) { throw new ValidationError(['ID doit être un nombre positif']); } // ... logique de récupération de l'utilisateur if (!user) { throw new NotFoundError('Utilisateur'); } return user;}
try { const user = getUser(-1);} catch (error) { if (err