12k
All articles

Améliorer la qualité du code JavaScript avec eslint-plugin-unicorn

eslint-plugin-unicorn pour ESLint 10 : installation en flat config, presets recommended ou unopinionated, et règles autofixables pour améliorer la qualité JS et TS.

OpenReplay Team
OpenReplay Team
Améliorer la qualité du code JavaScript avec eslint-plugin-unicorn

eslint-plugin-unicorn est un plugin ESLint volumineux et opinioné — plus de 300 règles — qui favorise les idiomes JavaScript et TypeScript modernes, détecte des bugs de qualité subtils, et dont la majorité des règles sont corrigibles automatiquement. Il requiert ESLint >=10.4, la configuration plate (flat config) et ESM, ce qui signifie que l’ancienne configuration .eslintrc avec extends: 'plugin:unicorn/recommended' n’est plus applicable. Si vous utilisez déjà ESLint avec une configuration de base et souhaitez améliorer la qualité de votre code sans écrire de règles personnalisées, ce plugin est la solution la plus rapide.

Le revers de la médaille, c’est que « opinioné » est un terme à double tranchant. Certaines règles préviennent des boucles accidentellement quadratiques et de véritables bugs de correction ; d’autres imposent des préférences stylistiques qui entreront en conflit avec une base de code existante. Ce guide distingue les deux : ce que le plugin applique, quelles règles méritent d’être conservées, lesquelles rétrograder ou désactiver, et un fichier eslint.config.mjs prêt à l’emploi qui fonctionne avec les outils actuels.

Points clés à retenir

  • eslint-plugin-unicorn propose plus de 300 règles, majoritairement corrigibles automatiquement et, depuis la v69 (juin 2026), requiert ESLint ≥10.4, la flat config et ESM — .eslintrc ne fonctionne plus car ESLint 10 l’a supprimé.
  • La voie d’adoption en une ligne : étendre unicorn/recommended, exécuter eslint --fix, puis désactiver la poignée de règles qui entrent en conflit avec votre base de code — ou partir du preset unopinionated, qui désactive d’emblée les règles opinionées.
  • Le plugin exporte trois presets : recommended, unopinionated (recommended sans les règles opinionées) et all.
  • prefer-set-has transforme les recherches .includes() dans une boucle — accidentellement quadratiques — en vérifications O(n) avec Set.has(), et no-array-for-each réécrit .forEach() en for…of pour un meilleur rétrécissement de type TypeScript.
  • Associez-le à @typescript-eslint/recommended-type-checked pour combiner l’application des idiomes avec le linting tenant compte des types.

Ce qu’est eslint-plugin-unicorn et sa philosophie

eslint-plugin-unicorn est un ensemble de règles ESLint qui orientent le code vers des patterns JavaScript et TypeScript modernes et moins sujets aux erreurs — en privilégiant les membres Set plutôt que les parcours de tableaux, for…of plutôt que .forEach(), les spécificateurs d’import node: plutôt que les noms de modules nus, et ainsi de suite. Il requiert ESLint >=10.4, la flat config et ESM. Le dépôt se décrit comme proposant plus de 300 règles, et la dernière version est la 69.0.0, avec des publications environ mensuelles.

Deux caractéristiques de conception influencent la façon dont vous l’adoptez. Premièrement, la plupart des règles sont corrigibles automatiquement, de sorte qu’une grande partie des violations se résolvent avec eslint --fix plutôt que par une édition manuelle. Deuxièmement, le plugin propose des configs de preset afin que vous n’ayez pas à activer les règles une par une. Le plugin exporte une config recommended qui applique les bonnes pratiques, et séparément une config all qui active toutes les règles, à l’exception des règles dépréciées. Le tableau des règles utilise des emojis pour indiquer le statut : ✅ pour les règles définies dans la configuration recommended, ☑️ pour la configuration unopinionated, et 🔧 pour les règles corrigibles automatiquement par l’option CLI --fix.

Le périmètre couvre principalement JavaScript et TypeScript, avec une remarque utile : bien que la plupart des règles ciblent JavaScript et TypeScript, certaines s’appliquent également à d’autres types de fichiers lorsqu’elles sont utilisées avec le plugin de langage ESLint correspondant, tel que @eslint/css, @eslint/json, @eslint/markdown ou @html-eslint/eslint-plugin. Pour la plupart des équipes, les règles JS/TS sont la raison principale de l’installation.

Installer eslint-plugin-unicorn avec la flat config et ESM

Installez le plugin et une version compatible d’ESLint, puis étendez le preset recommended dans une flat config eslint.config.mjs. La version requise est ESLint 10.x — ESLint 10 a supprimé entièrement le système eslintrc hérité, faisant de la flat config le seul format pris en charge, de sorte que tout tutoriel montrant .eslintrc.json avec extends: 'plugin:unicorn/recommended' est obsolète. ESLint requiert un runtime Node.js récent (Node 20.19+, 22.13+, ou 24+ selon la page npm d’ESLint).

npm install --save-dev eslint eslint-plugin-unicorn

Une configuration minimale fonctionnelle limitée aux fichiers JavaScript et TypeScript :

// eslint.config.mjs
import unicorn from 'eslint-plugin-unicorn';
import { defineConfig } from 'eslint/config';

export default defineConfig([
  {
    files: ['**/*.{js,mjs,cjs,ts,tsx}'],
    extends: [unicorn.configs.recommended],
    rules: {
      // les surcharges vont ici
    },
  },
]);

Cela reflète l’utilisation officielle : importer unicorn depuis 'eslint-plugin-unicorn', importer { defineConfig } depuis 'eslint/config', puis, dans defineConfig, définir files et extends: [unicorn.configs.recommended]. La limitation avec files est importante dès lors que vous lintez des langages non-JavaScript dans la même configuration — gardez les règles JavaScript de Unicorn dans leur propre objet de configuration.

Trois presets sont disponibles pour choisir votre point de départ :

  • recommended — la valeur par défaut soigneusement sélectionnée.
  • unopinionated — recommended sans les règles opinionées.
  • all — toutes les règles non dépréciées.

Si votre équipe trouve les règles opinionées trop bruyantes, remplacez unicorn.configs.recommended par unicorn.configs.unopinionated et évitez une grande partie des ajustements manuels décrits ci-dessous.

Une note de migration : si vous dépendez encore d’un plugin qui ne propose qu’une config au format eslintrc, encapsulez-le avec FlatCompat de @eslint/eslintrc. Utilisez la dernière version de @eslint/eslintrc avec ESLint 10.x.

Les règles eslint-plugin-unicorn à plus forte valeur ajoutée, avec exemples avant/après

Les règles Unicorn à plus forte valeur ajoutée méritent d’être adoptées en priorité, car chacune prévient un problème de performance, de correction ou de lisibilité plutôt que d’imposer un style. Chaque exemple ci-dessous correspond à ce que produit eslint --fix, et non à du code idéal écrit à la main.

prefer-set-has — élimine les recherches accidentellement quadratiques

prefer-set-has signale un tableau utilisé uniquement pour des vérifications d’appartenance avec .includes() et vous oriente vers un Set. Il préfère Set#has() à Array#includes() pour vérifier la présence ou l’absence d’un élément. Cela est particulièrement important dans les boucles : Array.prototype.includes est O(n) par appel, donc l’appeler une fois par itération sur une autre collection est O(n²), tandis que Set.prototype.has est approximativement en temps constant.

// ❌ O(n²) : includes() re-parcourt tout le tableau à chaque itération
const allowed = ['read', 'write', 'admin', /* …des centaines… */];
const granted = requested.filter((scope) => allowed.includes(scope));

// ✅ O(n) : Set.has() est approximativement en temps constant par recherche
const allowed = new Set(['read', 'write', 'admin', /* …des centaines… */]);
const granted = requested.filter((scope) => allowed.has(scope));

Le mode d’échec est silencieux : le code fonctionne bien sur de petits volumes et se dégrade fortement à mesure que les données croissent. Ce sont exactement les régressions de lenteur en interaction que vous finissez par diagnostiquer en production plutôt qu’en revue de code.

no-array-for-each — meilleur rétrécissement de type TypeScript et contrôle du flux

no-array-for-each réécrit les callbacks .forEach() en boucles for…of. Il préfère for…of à la méthode forEach. L’avantage va au-delà du style : for…of offre un meilleur rétrécissement de type TypeScript dans le corps de la boucle, permet d’utiliser await directement sans acrobaties de callback, et prend en charge break/continue — aucun de ces éléments ne fonctionne correctement dans un callback .forEach().

// ❌
users.forEach((user) => {
  sendEmail(user);
});

// ✅ corrigé automatiquement
for (const user of users) {
  sendEmail(user);
}

prefer-node-protocol — modules natifs Node sans ambiguïté

prefer-node-protocol réécrit les imports de modules natifs nus vers la forme explicite node:. Il préfère l’utilisation du protocole node: lors de l’import de modules natifs Node.js. Le préfixe explicite rend l’import sans ambiguïté comme module natif Node et le rend plus difficile à masquer par un paquet npm portant le même nom.

// ❌
import fs from 'fs';
import { join } from 'path';

// ✅ corrigé automatiquement
import fs from 'node:fs';
import { join } from 'node:path';

Quelques autres corrections automatiques à forte valeur ajoutée

Chacune fait correspondre un pattern hérité à son équivalent moderne et se corrige automatiquement :

// throw-new-error — Require new when throwing an error
throw Error('boom');        // ❌
throw new Error('boom');    // ✅

// prefer-string-replace-all — remplacement global sans regex /g
str.replace(/-/g, ' ');     // ❌
str.replaceAll('-', ' ');   // ✅

// prefer-array-flat-map — fusion de map().flat()
rows.map((r) => r.cells).flat();   // ❌
rows.flatMap((r) => r.cells);      // ✅

// new-for-builtins — require new for Map/Set/Array/Date, etc.
const cache = Map();        // ❌
const cache = new Map();    // ✅

La règle throw-new-error exige new lors de la création d’une erreur (couvrant à la fois const e = Error() et throw Error()), et elle figure dans la config recommended et est corrigible automatiquement. new-for-builtins impose l’utilisation de new pour tous les types natifs, à l’exception de String, Number, Boolean, Symbol et BigInt. Array.prototype.flatMap est à la fois plus clair et évite le tableau intermédiaire qu’alloue .map().flat().

Deux règles axées sur la lisibilité complètent l’ensemble à forte valeur ajoutée. better-regex améliore les expressions régulières en les rendant plus courtes et plus cohérentes (par exemple, [0-9] devient \d). prefer-ternary réduit les blocs if/else simples : il préfère les expressions ternaires aux instructions if simples qui retournent ou assignent des valeurs. Une instruction if-else produit généralement plus de lignes qu’un ternaire sur une seule ligne, et peut vous forcer à déclarer des variables avec let uniquement pour les réassigner dans les blocs — les laissant inutilement mutables et empêchant prefer-const de signaler la variable.

// ❌
function status(ok) {
  if (ok) {
    return 'pass';
  } else {
    return 'fail';
  }
}

// ✅ corrigé automatiquement
function status(ok) {
  return ok ? 'pass' : 'fail';
}

Les règles controversées et comment décider

Certaines règles recommended imposent des préférences plutôt que la correction, et elles génèrent le plus de bruit sur une base de code existante. Le tableau ci-dessous présente les décisions conserver/avertir/désactiver pour les suspects habituels, tous activés dans recommended.

RègleVerdictRaison
prevent-abbreviationsAvertir ou limiter la portéeRenomme eerror, reqrequest, propsproperties ; entre en conflit avec les noms déjà utilisés par votre framework et votre équipe.
no-nullDésactiver si vous utilisez nullUnicorn préfère undefined, mais null est légitime quand un ORM ou une API le retourne — il n’y a pas de réponse universellement correcte.
no-array-reduceConserver ou avertirInterdit Array#reduce() et Array#reduceRight() ; raisonnable pour la lisibilité, mais reduce convient à de nombreuses équipes.
filename-caseLimiter les exclusionsEntre en conflit avec les noms de fichiers imposés par le framework ; conservez-la mais excluez les exceptions.

Le cas no-null est le plus clair : Unicorn préfère undefined, pourtant null est ce que retournent de nombreux pilotes de base de données et API HTTP, de sorte que forcer l’un ou l’autre crée du bruit de conversion sans bénéfice en termes de correction. Désactivez-la quand null fait partie de votre contrat de données.

Vous n’avez pas à désactiver ces règles manuellement. Le preset unopinionated laisse les règles opinionées — dont no-null et prevent-abbreviations — désactivées par défaut, ce qui en fait le juste milieu intégré entre recommended et une config fortement personnalisée.

Pour les règles que vous conservez mais souhaitez ajuster, limitez leur portée plutôt que de les désactiver. Un pattern courant pour filename-case exclut les fichiers du framework :

{
  files: ['**/*.{js,ts,tsx}'],
  extends: [unicorn.configs.recommended],
  rules: {
    'unicorn/no-null': 'off',
    'unicorn/prevent-abbreviations': 'warn',
    'unicorn/filename-case': [
      'error',
      {
        cases: { pascalCase: true, camelCase: true },
        ignore: ['next-env\\.d\\.ts', 'vite\\.config\\.ts'],
      },
    ],
  },
}

Adoption progressive sur une base de code existante

Déployez le plugin par étapes afin que la première exécution ne vous noie pas sous les erreurs. La séquence qui fonctionne :

  1. Partez d’un preset. Étendez unicorn/recommended, ou unicorn/unopinionated si les règles opinionées sont trop bruyantes pour votre équipe.
  2. Commencez par la correction automatique. Exécutez npx eslint . --fix. Comme la plupart des règles sont corrigibles automatiquement, une grande fraction des violations disparaît ici, et vous examinez un seul diff mécanique plutôt que des centaines d’erreurs en ligne.
  3. Rétrogradez les règles bruyantes en warn. Tout ce qui continue à se déclencher en volume sans être un problème de correction — prevent-abbreviations est le coupable habituel — passe à 'warn' pour ne pas bloquer la CI pendant la migration.
  4. Limitez la portée plutôt que de désactiver complètement. Utilisez l’option ignore (comme avec filename-case) et les globs files pour créer des exceptions plutôt que de supprimer une règle utile partout.
  5. Ajoutez le linting tenant compte des types. Ajoutez @typescript-eslint/recommended-type-checked aux côtés de Unicorn. Unicorn applique les idiomes ; la config type-checked de typescript-eslint détecte les bugs que seul le système de types peut voir. Les deux sont complémentaires, et les activer ensemble est une valeur par défaut solide pour un projet TypeScript.

Traitez votre config comme un document vivant. À chaque mise à jour du plugin — les versions arrivent environ mensuellement — revérifiez le tableau des règles pour les nouvelles règles et les renommages, et réévaluez les règles en warn prêtes à passer en error.

La voie la plus rapide vers un meilleur JavaScript avec eslint-plugin-unicorn est sans glamour : étendre unicorn/recommended, exécuter eslint --fix, et concentrer votre attention uniquement sur la poignée de règles opinionées qui entrent véritablement en conflit avec la façon dont votre équipe écrit le code. Commencez avec le preset aujourd’hui, laissez le correcteur automatique faire l’essentiel du travail, et affinez à partir de là — le tableau officiel des règles est la référence pour le statut et les options actuels de chaque règle.

FAQ

eslint-plugin-unicorn fonctionne-t-il avec typescript-eslint, ou y a-t-il des conflits ?

Ils sont complémentaires et conçus pour fonctionner ensemble. Unicorn applique les idiomes JavaScript et TypeScript modernes, tandis que typescript-eslint ajoute des règles tenant compte des types qui détectent des bugs que seul le système de types peut voir. Ajoutez les deux en tant qu'objets de config séparés dans votre flat eslint.config.mjs et associez unicorn/recommended à la config recommended-type-checked de typescript-eslint. Les activer tous les deux est une valeur par défaut solide pour un projet TypeScript, sans chevauchement de règles à résoudre.

Quelle est la différence entre les presets recommended et unopinionated ?

Le preset recommended est la valeur par défaut soigneusement sélectionnée qui active à la fois les règles de correction et les règles stylistiques opinionées. Le preset unopinionated correspond à recommended sans les règles opinionées, désactivant d'emblée les entrées controversées comme no-null et prevent-abbreviations qui entrent en conflit avec les bases de code existantes. Choisissez unopinionated quand votre équipe trouve les règles stylistiques trop bruyantes ; c'est le juste milieu intégré entre recommended et une config fortement personnalisée. Un troisième preset, all, active toutes les règles non dépréciées.

Pourquoi mon ancienne config .eslintrc avec extends plugin:unicorn/recommended ne fonctionne-t-elle plus ?

ESLint 10 a supprimé entièrement le système eslintrc hérité, faisant de la flat config le seul format pris en charge, et eslint-plugin-unicorn requiert ESLint 10.4 ou supérieur, la flat config et ESM. L'ancienne syntaxe .eslintrc avec la chaîne extends 'plugin:unicorn/recommended' ne se charge plus. Migrez vers un eslint.config.mjs qui importe unicorn depuis 'eslint-plugin-unicorn' et définit extends sur unicorn.configs.recommended dans un objet de config limité par files.

L'ajout d'eslint-plugin-unicorn va-t-il casser mon build avec des centaines d'erreurs ?

La plupart des violations se résolvent automatiquement car la majorité des règles unicorn sont corrigibles automatiquement. Exécutez d'abord eslint avec l'option fix, ce qui élimine une grande fraction des problèmes sous forme d'un seul diff mécanique plutôt que des centaines d'erreurs en ligne. Pour les règles qui continuent à se déclencher en volume sans être des problèmes de correction, comme prevent-abbreviations, définissez-les sur warn afin qu'elles ne bloquent pas la CI pendant la migration. Ce déploiement progressif maintient le build au vert pendant que vous adoptez le plugin.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.