Pourquoi `catch ()` provoque une SyntaxError en JavaScript
Découvrez pourquoi une liste de paramètres catch vide empêche complètement l’analyse du JavaScript, et découvrez les deux manières grammaticalement correctes d’écrire un bloc catch sans paramètres.
À première vue, ce fragment semble afficher « Error ». En réalité, tout le fichier génère une erreur de syntaxe, car l’écriture de catch () sans rien entre les parenthèses n’a jamais été une syntaxe JavaScript valide.
Imaginez un entretien de deuxième tour pour un poste front-end. Ce qui semblait être une question d’échauffement s’est avéré plus difficile. L’entreteneur a demandé un exemple de structure try-catch qui lance une erreur et enregistre un message dans le bloc catch, puis a ajouté un indice : « vous n’avez pas besoin de l’objet d’erreur. »
Si l’on n’a pas besoin de l’objet d’erreur, la démarche naturelle est d’éviter de le déclarer. Des années à écrire des codes du type function handler() {} apprennent à ne pas remplir la liste des paramètres.
try {
throw "Error";
} catch () {
console.log('Error')
}
Lorsqu’on demande quel sera le résultat, la réponse évidente semble être « Error » : la chaîne est lancée, le bloc catch s’exécute et console.log est appelé. Mais lorsque l’intervieweur a fait exécuter réellement le code :
Uncaught SyntaxError: Unexpected token ')'
Rien n’est affiché. Ni « Error », ni rien d’autre. Le script ne met jamais en exécution une seule instruction. C’est alors que l’intervieweur a prononcé la phrase dont tire son titre cet article :
You have five years of experience in JavaScript and don’t know how the try-catch block works.
Ce n’était pas formulé comme une question. C’était une affirmation, et une affirmation dérangeante, car elle s’avérait exacte. Au cours de cinq ans passés à écrire du JavaScript, ce développeur n’avait jamais tapé catch (). Le paramètre avait toujours un nom, même dans les cas où il n’était jamais réellement référencé. La première tentative pour l’omettre a révélé une règle qui n’avait tout simplement jamais été apprise.
catch possède exactement deux formes valides
La grammaire formelle de la clause catch (ECMA-262, section 14.15, The try Statement) est concise :
Catch :
catch ( CatchParameter ) Block
catch Block
CatchParameter :
BindingIdentifier
BindingPattern
Examinez de près la première règle de production. Lorsque des parenthèses sont présentes, un CatchParameter est obligatoire à l’intérieur d’elles. Rien dans la grammaire ne le désigne comme optionnel. Il doit correspondre à exactement une liaison : un identifiant simple tel que e, ou un schéma de déstructuration comme {message}. Une paire de parenthèses vide ne correspond à aucune de ces règles.
La deuxième règle de production, introduite avec ES2019, supprime complètement les parenthèses. Des parenthèses vides ne correspondent à aucune règle non plus. Ainsi, lorsque le analyseur lit catch (, il s’attend à un token de liaison valide juste après, mais rencontre plutôt ), ce qui provoque une erreur. C’est précisément le message affiché par V8 : Unexpected token ')'.
Cela soulève une question légitime : pourquoi function f() {} se compile-t-il sans problème tandis que catch () {} ne fonctionne jamais ? La liste des paramètres d’une fonction suit la grammaire FormalParameters, qui autorise zéro paramètre, des valeurs par défaut et une syntaxe de type rest. CatchParameter a été conçu intentionnellement différemment : il représente exactement un seul lien obligatoire, sans liste séparée par des virgules, sans valeurs par défaut et sans élément rest. Catch ne prend pas en compte l’idée d’une liste de paramètres vide, donc le raccourci mental consistant à « simplement vider les parenthèses », qui fonctionne pour les fonctions, ne s’applique pas ici.
Cette erreur se produit avant que votre code ne s’exécute
Il y avait une deuxième idée fausse cachée dans cette erreur : considérer les erreurs comme un phénomène purement lié au temps d’exécution. Cette panne se produit pendant le analyse syntaxique, avant même que l’exécution ne commence. Le moteur parcourt tout le script, n’arrive pas à le faire correspondre à la grammaire, et signale une SyntaxError avant même qu’une seule ligne n’ait eu la chance d’être exécutée. Tout le reste dans ce fichier est affecté par cela.
Ajouter une ligne de plus rend cela évident. Ce qui suit a été confirmé dans Chrome :
console.log('before'); // NEVER runs
try { throw "Error"; } catch() { }
Uncaught SyntaxError: Unexpected token ')'
La ligne console.log('before') se trouve au-dessus de la clause catch défectueuse et n’a rien de problématique en soi, pourtant elle n’est jamais affichée. Tout le script échoue à se compiler, donc rien ne s’exécute.
Cela mène à une conclusion facile à manquer au premier abord : un bloc try-catch à l’intérieur d’un fichier ne peut pas capturer un SyntaxError provenant de ce même fichier. Pour que tout bloc catch s’exécute, le code qui l’entoure devrait déjà avoir été analysé avec succès, ce qui est justement ce qui a échoué. La seule façon de capturer ce type d’erreur est depuis une unité de compilation distincte, via eval, new Function ou un import() dynamique.
Les deux manières de l’écrire correctement
Les deux approches ci-dessous ont été testées dans Chrome, et toutes deux sont valides.
Option 1 : conserver le paramètre, sans jamais l’utiliser. Déclarer un paramètre catch que l’on ne référence jamais est tout à fait légal, et cela l’a toujours été, dans toutes les versions d’ECMAScript.
try {
throw "Error";
} catch (e) {
console.log('caught, e unused');
}
// logs: caught, e unused
Option 2 : supprimer complètement les parenthèses. Il s’agit de la syntaxe d’association optionnelle catch de ES2019 : pas de parenthèses, pas de paramètre, simplement catch {.
try {
throw "Error";
} catch {
console.log('caught without binding');
}
// logs: caught without binding
Le modèle mental à retenir : raccourcir catch (e) signifie supprimer les parenthèses, et non éliminer ce qui se trouve à l’intérieur. Le point intermédiaire entre ces deux formes valides est précisément celui où se produit l’erreur SyntaxError.
D’où provient la syntaxe catch sans parenthèses
L’association optionnelle catch a débuté comme une proposition du TC39 rédigée par Michael Ficarra. Elle est arrivée à la phase 4 en janvier 2018 et a fait partie d’ES2019. Le support des navigateurs et des environnements de exécution a suivi rapidement : Chrome 66, Firefox 58, Safari 11.1 et Node.js 10 la prennent en charge, ce qui signifie qu’elle peut être utilisée sans risque dans pratiquement n’importe quel environnement d’aujourd’hui.
Le raisonnement derrière cette proposition correspond exactement au scénario qui a posé problème au candidat à l’entretien : un code dans lequel l’erreur capturée n’est vraiment jamais nécessaire. Pensez à la vérification pour savoir si une chaîne se transforme en JSON valide et au recours à une valeur par défaut en cas d’échec, ou encore aux schémas de détection de fonctionnalités où le simple fait de capturer l’exception vous donne déjà toutes les informations nécessaires. Dans de tels cas, nommer l’erreur e ne crée qu’une variable qui est assignée mais jamais lue — et la proposition elle-même souligne que ce schéma indique généralement la présence d’une erreur ailleurs dans le code.
Il est important d’être précis quant aux changements réels apportés par cette proposition : elle a introduit une nouvelle production grammaticale pour un bloc catch ne contenant absolument aucune liste de paramètres. Elle n’a pas légalisé l’usage de parenthèses vides. Les parenthèses ne sont pas vidées — elles sont complètement supprimées. Cela permet de respecter la règle selon laquelle CatchParameter, lorsqu’il est présent, doit correspondre à une seule et unique liaison, et cela rend catch { visuellement cohérent avec finally {, un bloc qui n’a jamais eu de parenthèses à l’origine.
Pourquoi même les développeurs expérimentés tombent dans ce piège
Il y a trois raisons distinctes qui font que les gens se trompent à ce sujet, et aucune d’elles ne relève d’un manque d’effort ou d’étude.
Vos instincts ici proviennent d’un schéma différent, plus familier — et ils vous trompent. Chaque signature de fonction que vous avez jamais écrite renforce l’idée qu’une liste de paramètres non utilisés peut être réduite à des parenthèses vides : function () {} est tout à fait normal. Comme une clause catch ressemble visuellement à un en-tête de fonction, il semble naturel d’appliquer le même raccourci. Mais une clause catch ne prend pas de liste de paramètres — elle prend une seule liaison — et la grammaire n’a tout simplement jamais inclus de variante vide pour elle.
Cette erreur ne se manifeste généralement qu’au moment précis où l’on tente de supprimer un e non utilisé. Ce moment est souvent déclenché par une alerte de linter. La règle no-unused-vars d’ESLint comprend une option caughtErrors, et à partir d’ESLint 9, cette option a pour valeur par défaut "all" — ce qui signifie qu’un catch (e) non utilisé génère automatiquement une erreur de lintage. Pour corriger cette alerte, les développeurs vident instinctivement les parenthèses comme ils le feraient pour une signature de fonction, et c’est précisément là que le parseur les arrête. La correction véritablement correcte — supprimer complètement les parenthèses et écrire catch { — est celle que presque personne n’essaie en premier, car ailleurs en JavaScript, « raccourcir » ne signifie pas supprimer mais plutôt vider.
Ce bug ne survit jamais assez longtemps pour laisser une trace. Une erreur logique subtile peut se glisser en production et hanter un code pendant des mois, devenant le genre d’histoire que les développeurs racontent pendant des années. Celui-ci n’est pas du tout comme ça : il s’agit d’un SyntaxError détecté immédiatement au moment du parsing. On voit le soulignement ondulé, on le corrige en quelques secondes et on passe à autre chose sans y penser davantage. Il n’y a aucun souvenir durable de cet incident — c’est précisément pourquoi c’est une excellente question pour un entretien. Elle permet de vérifier la frontière entre la syntaxe que l’on a véritablement assimilée et celle que l’on croit seulement comprendre.
Un point important avant de commencer à réécrire tous les catch (e) que vous trouvez : catch { } vous permet d’éviter de donner un nom à l’erreur, mais pas d’éviter de la gérer. La forme ES2019 existe pour une logique de vérification et de fallback légitime où le simple fait d’intercepter l’erreur constitue le signal utile. Un corps de bloc catch vide qui ignore silencieusement une véritable panne est tout aussi problématique qu’avant, avec ou sans parenthèses.
La réponse qui aurait été plus appropriée
La réponse qui aurait permis de maintenir cette interview sur la bonne voie : « Cela provoque une SyntaxError au moment de l’analyse — plus précisément Unexpected token ')'. Rien dans le fichier ne s’exécute du tout, pas même le code qui se trouve avant le bloc try. Une clause catch ne peut prendre que deux formes légales : catch (binding) { }, ou, depuis ES2019, la forme sans paramètre catch { }. Des parenthèses vides ne correspondent à aucune de ces formes. »
Points clés à retenir :
catch ()avec des parenthèses vides a toujours constitué, et constitue encore aujourd’hui, une SyntaxError dans toutes les versions de JavaScript.- Il n’existe que deux formes valides :
catch (e) { }avec un seul paramètre nommé, oucatch { }sans aucune parenthèse, introduite en ES2019.
catch (e) avec une variable e non utilisée est techniquement autorisé, mais ESLint 9 la détecte automatiquement grâce à l’option no-unused-vars ; la correction appropriée consiste à supprimer les parenthèses, et non à effacer leur contenu.catch { }, qui ne prend pas de paramètre, existe pour les cas où la valeur de l’erreur est véritablement inutile — ce n’est pas une excuse générale pour ignorer silencieusement des erreurs réelles.La réaction sévère du interviewer était-elle justifiée ? Seulement en partie. Le comportement en temps de exécution des blocs try-catch n’a jamais été remis en question — cette partie faisait partie intégrante de la logique. Ce qui n’a jamais vraiment été examiné, c’est la grammaire elle-même, simplement parce que le code utilisé au quotidien ne vous oblige que rarement à y faire face. De nos jours, l’habitude a changé : ajouter un e non utilisé signifie désormais supprimer les parenthèses avec lui, et non simplement les vider.
Lorsque vous raccourcissez une clause catch, supprimez les parenthèses — ne les videz jamais.
Lectures complémentaires
- Un ensemble de hooks personnalisés réutilisables pour chaque nouveau projet React — Découvrez une sélection de hooks personnalisés pour React couvrant le stockage, le débouncing, les clics et la récupération de données, afin d’éliminer le code générique répétitif dans les nouveaux projets.