← Retour aux articles
Tooling ESLintTypeScriptDX

ESLint : imposer la qualité de code sur les projets TypeScript

· 4 min de lecture

Les code reviews ne devraient pas perdre du temps sur des débats de formatage ou d’ordre des imports. ESLint automatise l’application des règles de qualité de code sur un projet TypeScript pour que les développeurs se concentrent sur la logique, l’architecture et les vrais bugs. Avec le format flat config introduit en v9, configurer ESLint est plus propre que jamais, et l’intégration TypeScript a mûri au point où les règles type-aware attrapent des problèmes que le compilateur seul ne détecte pas.

Configuration flat config

La flat config remplace l’ancienne chaîne .eslintrc par un seul fichier eslint.config.ts. Plus de cascading de config, plus de tableaux extends, plus de confusion sur les règles qui s’appliquent où.

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  {
    plugins: { react: reactPlugin, 'react-hooks': reactHooksPlugin },
    rules: {
      'react/jsx-no-target-blank': 'error',
      'react-hooks/rules-of-hooks': 'error',
      'react-hooks/exhaustive-deps': 'warn',
    },
  },
  { ignores: ['.next/', 'node_modules/', 'dist/'] }
);

recommendedTypeChecked active les règles qui utilisent les informations de type du compilateur TypeScript. Elles attrapent de vrais bugs comme l’usage unsafe de any, les promesses flottantes et les hypothèses de nullabilité incorrectes que le linting basique rate complètement.

Des règles type-aware qui attrapent de vrais bugs

Les règles ESLint standard opèrent sur la syntaxe. Les règles type-aware opèrent sur la sémantique. La différence, c’est attraper un await manquant sur une fonction qui retourne une Promise au lieu de l’ignorer parce que la syntaxe est du JavaScript valide.

// @typescript-eslint/no-floating-promises attrape ça
async function deleteProject(id: string) {
  // Bug : la promise n'est pas awaitée, les erreurs sont avalées silencieusement
  db.project.delete({ where: { id } });
}

// @typescript-eslint/no-unsafe-assignment attrape ça
const config: Config = JSON.parse(rawInput); // any assigné à une variable typée

no-floating-promises à lui seul justifie l’activation du linting type-aware. Les promesses non gérées sont l’une des sources les plus courantes de failures silencieuses dans les applications Node.js et Next.js. Le compilateur TypeScript ne les signale pas, mais ESLint oui.

Règles custom pour les conventions du projet

Chaque codebase a des conventions qu’aucun plugin public ne couvre. Peut-être que les barrel imports sont interdits pour des raisons de performance, ou que certains répertoires ne doivent importer que depuis des modules spécifiques. L’API de règles ESLint permet d’encoder ça en checks automatisés.

{
  rules: {
    'no-restricted-imports': [
      'error',
      {
        patterns: [
          {
            group: ['@/features/*/internal/*'],
            message: 'Import from the feature public API, not internal modules.',
          },
        ],
      },
    ],
    'no-restricted-syntax': [
      'error',
      {
        selector: 'CallExpression[callee.name="useEffect"][arguments.length=1]',
        message: 'useEffect must have a dependency array.',
      },
    ],
  },
}

no-restricted-imports impose les frontières entre modules. no-restricted-syntax utilise des sélecteurs AST pour interdire des patterns de code spécifiques. Ensemble, ils transforment les décisions architecturales en garde-fous automatisés qui attrapent les violations au lint plutôt que pendant la code review.

Intégration CI

Lancer ESLint en local est optionnel. Le lancer en CI ne l’est pas. Une étape lint dans le pipeline garantit qu’aucun code ne merge sans passer les règles, quel que soit le setup éditeur individuel ou les pre-commit hooks oubliés.

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npx eslint . --max-warnings 0

--max-warnings 0 traite les warnings comme des erreurs en CI. Ça empêche l’accumulation progressive de warnings que tout le monde ignore jusqu’à en avoir des centaines. Les règles sont soit appliquées, soit elles ne le sont pas. La zone grise des warnings ne fonctionne que pendant les périodes de migration.

Conclusion

ESLint transforme les conventions de code d’opinions en checks automatisés. Les règles type-aware attrapent des bugs que le compilateur rate, les règles custom imposent les frontières architecturales, et l’intégration CI rend la conformité non négociable. Pour les projets TypeScript et React, un ESLint bien configuré est l’outil le plus efficace pour maintenir la qualité de code à travers une équipe.