8000 Docs: Clarify how the `config()` helper handles rule merging in flat config · Issue #11220 · typescript-eslint/typescript-eslint · GitHub
[go: up one dir, main page]

Skip to content

Docs: Clarify how the config() helper handles rule merging in flat config #11220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
8000 2 tasks done
wujekbogdan opened this issue May 15, 2025 · 1 comment
Closed
2 tasks done
Labels
documentation Documentation ("docs") that needs adding/updating triage Waiting for team members to take a look

Comments

@wujekbogdan
Copy link
< 8000 /tr>

Before You File a Documentation Request Please Confirm You Have Done The Following...

Suggested Changes

Here's my real-life config:

import jsEslint from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier/flat";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import globals from "globals";
import tsEslint from "typescript-eslint";

export default tsEslint.config(
  jsEslint.configs.recommended,
  tsEslint.configs.recommendedTypeChecked,
  tsEslint.configs.stylisticTypeChecked,
  reactHooks.configs["recommended-latest"],
  reactRefresh.configs.recommended,
  reactRefresh.configs.vite,
  {
    ignores: ["dist"],
  },
  {
    languageOptions: {
      parserOptions: {
        project: ["./tsconfig.node.json", "./tsconfig.app.json"],
        tsconfigRootDir: import.meta.dirname,
      },
      ecmaVersion: 2020,
      globals: globals.browser,
    },
  },
  {
    files: ["**/*.js", "**/*.mjs"],
    extends: [tsEslint.configs.disableTypeChecked],
  },
  eslintConfigPrettier,
);

After setting it up, it turned out there's just one rule I'd like to override, so I turned my config into:

export default tsEslint.config(
  // ... configs
  {
    ignores: ["dist"],
  },
  {
    languageOptions: {
      parserOptions: {
        project: ["./tsconfig.node.json", "./tsconfig.app.json"],
        tsconfigRootDir: import.meta.dirname,
      },
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    rules: {
     // The rule I added:
      "@typescript-eslint/consistent-type-definitions": "off",
    },
  },
  {
    files: ["**/*.js", "**/*.mjs"],
    extends: [tsEslint.configs.disableTypeChecked],
  },
  eslintConfigPrettier,
);

I expected this to simply merge with the existing rules, but it turned out it overwrote all the previously set rules, including react-hooks/rules-of-hooks and react-refresh/only-export-components.

My first instinct was to manually re-add all the previously defined rules:

export default tsEslint.config(
  // ... configs
  {
    ignores: ["dist"],
  },
  {
    languageOptions: {
      parserOptions: {
        project: ["./tsconfig.node.json", "./tsconfig.app.json"],
        tsconfigRootDir: import.meta.dirname,
      },
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    rules: {
      // The rules I brought back:
      "react-hooks/rules-of-hooks": "error",
      "react-hooks/exhaustive-deps": "warn",
      "react-refresh/only-export-components": "error",
      "@typescript-eslint/consistent-type-definitions": "off",
    },
  },
  {
    files: ["**/*.js", "**/*.mjs"],
    extends: [tsEslint.configs.disableTypeChecked],
  },
  eslintConfigPrettier,
);

It worked, but the manual process didn't feel right or elegant, so I started fiddling around and figured out that I have to append the rules into the next array item, instead of adding it to the entry that contains languageOptions.

I ended up with:

import jsEslint from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier/flat";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import globals from "globals";
import tsEslint from "typescript-eslint";

export default tsEslint.config(
  // ... configs
  {
    ignores: ["dist"],
  },
  {
    languageOptions: {
      parserOptions: {
        project: ["./tsconfig.node.json", "./tsconfig.app.json"],
        tsconfigRootDir: import.meta.dirname,
      },
      ecmaVersion: 2020,
      globals: globals.browser,
    },
  },
  {
    rules: {
      "@typescript-eslint/consistent-type-definitions": "off",
    },
  },
  {
    files: ["**/*.js", "**/*.mjs"],
    extends: [tsEslint.configs.disableTypeChecked],
  },
  eslintConfigPrettier,
);

It was just a trial-and-error process that led me to that solution, but after figuring it out I still don't really understand why it worked. Why does having rules together with languageOptions prevent merging, whereas a separate entry containing just the rules object is correctly merged?


What I'm proposing is adding a very explicit explanation on how to add custom rule overrides in a safe way - so that preceding rules aren't overridden OR, and maybe it's a better option, clarify how the tsEslint.config() performs merging: what's being merged and what is not.

Affected URL(s)

https://typescript-eslint.io/packages/typescript-eslint#config

Additional Info

No response

@wujekbogdan wujekbogdan added triage Waiting for team members to take a look documentation Documentation ("docs") that needs adding/updating labels May 15, 2025
@bradzacher
Copy link
Member
bradzacher commented May 15, 2025

The config helper doesn't merge anything.
It is a simple helper to make it easier to declare configs by providing extends or auto-flattening arrays to avoid the need for spreads.

The examples show you the equivalent code you would write without using the config util.

The config merging logic is all part of eslint itself.

https://eslint.org/docs/latest/use/configure/

If includes tools like --inspect-comfig for debugging your config
https://eslint.org/docs/latest/use/command-line-interface#--inspect-config

@bradzacher bradzacher closed this as not planned Won't fix, can't repro, duplicate, stale May 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Documentation ("docs") that needs adding/updating triage Waiting for team members to take a look
Projects
None yet
Development

No branches or pull requests

2 participants
0