How to correctly configure the parser & plugins with ESLint's new Flat-config?

7.9k views Asked by At

How does one go about configuring ESLint using the new Flat-Config system (aka eslint.config.js file) such that it works with the @TypeScript-ESLint/ESLint-plugin & TypeScript parser?


ESLint's new configuration system, "Flat Config" allows for multiple rule-sets in a single configuration file. This makes overriding rules much easier than using the cascading file-system concept that ESLint implements in its original configuration system. Because I have to add support for both ESM & CJS when I publish a package to NPM, the Flat-config is like a dream come true, as it will allow me to use ESLint in a far more simple manner.

The only problem is I cannot get ESLint's new configuration system to work with any plug-ins, and on the opposite side of the coin, I cannot get plugins to work with flat-config.

When Linting TypeScript using ESLint, its pretty important to equip the 2 plug-ins I listed below. Those two plug-ins implement rules that allows typescript to be able to lint TypeScript code bases correctly. Its imperative that I have them.

2BH I am all over the place with my configuration file, currently it looks like the example bellow, but the example just shows my latest desperate attempt, I have tried all sorts of things.

import eslintPlugin from '@typescript-eslint/eslint-plugin'

export default [
  {
    files: ["src/**/*.ts", "src/main.cts", "src/main.mts"],
    ignores: ["**/*.d.*", "**/*.map.*", "**/*.js", "**/*.mjs", "**/*.cjs"],
    plugins: { eslintPlugin },

    languageOptions: {
      ecmaVersion: "latest",
      sourceType: "module",
      parser: "eslintPlugin/parser",
    },

    rules: {
      semi: "error",
      quotes: ["error", "single"],
      indent: [
        "error",
        2,
        {
          SwitchCase: 1,
          VariableDeclarator: "first",
          ImportDeclaration: "first",
          ArrayExpression: "first",
          ObjectExpression: "first",
          CallExpression: { arguments: "first" },
          FunctionDeclaration: { body: 1, parameters: 4 },
          FunctionExpression: { body: 1, parameters: 4 },
        },
      ],
    },
  },

];



I also use the TypeScript plugin TypeScript ESLint Language Service plug-in. I don't like when TypeScript & ESLint report the same errors, so I don't use any extension for ESLint, but I do integrate a build system that lints my project as work on, and the errors found by ESLint are reported through the TSC compiler.

The problem is the same though, I cannot get the "TypeScript/ESLint language service" plug-in to work with the new ESLint Configuration System (aka flat-config).


If anyone knows how to configure eslint using the eslint.config.js flat config file so that it works with the typescript/eslint plug-ins (plug-ins for eslint) &/or the "TS Language Service" plugin (plugin for typescript) I would like if you could show me what that configuration looks like.

5

There are 5 answers

5
Tom On BEST ANSWER

Im using flat config with typescript. Heres what I think are the important parts of the config:

import ts from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import functional from 'eslint-plugin-functional';
import imprt from 'eslint-plugin-import'; // 'import' is ambiguous & prettier has trouble


...


    languageOptions: {
      parser: tsParser,
      parserOptions: {
        ecmaFeatures: { modules: true },
        ecmaVersion: 'latest',
        project: './tsconfig.json',
      },
    },
    plugins: {
      functional,
      import: imprt,
      '@typescript-eslint': ts,
      ts,
    },

...

    rules: {
      ...ts.configs['eslint-recommended'].rules,
      ...ts.configs['recommended'].rules,

      'ts/return-await': 2,


Take note that the ts plugin is there twice. The shared configs use the longer namespace than what I wanted to use.

1
AudioBubble On

This works for me but I still find the API to be rather verbose.

const tsPlugin = require("@typescript-eslint/eslint-plugin");
const tsParser = require("@typescript-eslint/parser");

const tsOverrideConfig = tsPlugin.configs["eslint-recommended"].overrides[0];
const tsRecommemdedConfig = tsPlugin.configs.recommended;
const files = ["**/*.ts", "**/*.tsx"];

module.exports = [
  "eslint:recommended",
  {
    files,
    linterOptions: {
      reportUnusedDisableDirectives: true,
    },
    languageOptions: {
      parser: tsParser,
    },
    plugins: {
      "@typescript-eslint": tsPlugin,
    },
  },
  { files, rules: tsOverrideConfig.rules },
  { files, rules: tsRecommemdedConfig.rules },
];
0
vitalets On

Here is my working flat eslint.config.mjs with the following key points:

import globals from "globals";
import tsParser from "@typescript-eslint/parser";
import tsPlugin from "@typescript-eslint/eslint-plugin";
import eslintJsPlugin from "@eslint/js";

export default [
  {
    rules: eslintJsPlugin.configs.recommended.rules,
  },
  {
    files: [ "**/*.ts" ],
    languageOptions: {
      parser: tsParser,
      globals: globals.node,
    },
    plugins: {
      "@typescript-eslint": tsPlugin,
    },
    rules: {
      ...tsPlugin.configs.recommended.rules,
      "no-console": "error",
    }
  },
  {
    files: [ "test/**/*.ts" ],
    languageOptions: {
      globals: globals.mocha,
    },
  },
];

To run eslint with this config in CommonJS project I use the following command:

ESLINT_USE_FLAT_CONFIG=true npx eslint src test
0
Exodus 4D On

Check out the flatConfig example in microsoft/vscode-eslint repository (➜ VSCode ESLint extension).

Example: eslint.config.js

const globals = require('globals');
const typescriptParser =  require('@typescript-eslint/parser');
const typescriptPlugin = require('@typescript-eslint/eslint-plugin');

module.exports = [
    "eslint:recommended",
    {
        files: ["**/*.js"],
        languageOptions: {
            parserOptions: {
                sourceType: "module"
            },
            globals: {
                ...globals.browser,
                ...globals.node,
                ...globals.es6,
                ...globals.commonjs
            }
        },
    },
    {
        files: ["sub/*.js"],
        rules: {
            "no-undef": "warn",
            "no-console": "warn"
        }
    },
    {
        files: ["*.ts", "**/*.ts"],
        plugins: {
            "@typescript-eslint": typescriptPlugin
        },
        languageOptions: {
            parser: typescriptParser,
            parserOptions: {
                project: "./tsconfig.json",
                sourceType: "module",
                ecmaVersion: 2020
            }
        },
        rules: {
            "semi": "off",
            "@typescript-eslint/semi": "error",
            "no-extra-semi": "warn",
            "curly": "warn",
            "quotes": ["error", "single", { "allowTemplateLiterals": true } ],
            "eqeqeq": "error",
            "indent": "off",
            "@typescript-eslint/indent": ["warn", "tab", { "SwitchCase": 1 } ],
            "@typescript-eslint/no-floating-promises": "error"
        }
    }
]
ESLint blog posts (flat config):
  1. ESLint's new config system, Part 1: Background
  2. ESLint's new config system, Part 2: Introduction to flat config (most interesting)
  3. ESLint's new config system, Part 3: Developer preview
0
flexiness On

Perhaps worth mentioning typescript-eslint suggests another approach in defining their eslint Flat Config:

Example: eslint.config.js

// @ts-check

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.recommended,
);

https://typescript-eslint.io/getting-started/