ESLintのflat configは分割して書くといいかも

# ESLintのFlat Configとは

ESLintのFlat Configは、ESLintの設定ファイルの新しい書き方です。

従来の`.eslintrc.js` `.eslintrc.json`に代わり、`eslint.config.js`というファイルにJavaScript形式で書いていきます。

https://eslint.org/blog/2022/08/new-config-system-part-1/

https://eslint.org/docs/latest/use/configure/configuration-files-new

https://zenn.dev/makotot/articles/0d9184f3dde858

https://zenn.dev/babel/articles/eslint-flat-config-for-babel

Flat Configの詳しい解説は上で紹介した方々の記事に譲りますが、今後設定ファイルを書く際はFlat Configが推奨されるそうなので、早いうちに移行しときたいですね。

# Flat Configのポイント：カスケードさせる

Flat Configでは、複数の設定を組み合わせる方法が従来と変わります。

従来の`eslintrc`では、設定を組み合わせる際に`overrides`と`extends`による**継承**を行っていました。
新しいFlat Configでは、設定は全てJavaScriptの配列で管理されます。そのため、設定項目を見やすい形で分割して組み合わせることができます。（これは**フラットカスケード**と呼ばれています (<a href="https://eslint.org/blog/2022/08/new-config-system-part-2/#goodbye-extends%2C-hello-flat-cascade">出典</a>)。）

```js:.eslintrc (従来)
{
  "extends": ["./other-config.json"],
  "overrides": [
    { /* 上書きしたい設定項目 */ }
  ]
}
```

```js:eslint.config.js (新)
// 設定項目は分割して配列に入れる
// 上から順に適用されていく
export default [
  { /* 設定項目1 */ },
  { /* 設定項目2 */ },
  { /* 設定項目3 */ },
  { /* 設定項目4 */ },
  ...
];
```

Flat ConfigはただのJavaScriptファイルなので、設定を別ファイルに切り出したい時は`import`か`require`で変数として読み込むだけOKです。

設定項目を簡単に分割できるようになったことで、

- 対象ファイルの設定
- 使用したいルールの設定
- 言語やパーサーの設定
- プラグインの設定

などを分割して書けるようになりました。以下では実際の`eslint.config.js`がどのように記述されるのか、一例を紹介します。

## eslint.config.jsの書き方の例

以下のJSファイルは、`typescript-eslint`プラグインを使用したeslintルールをFlat Configで記述したものになります。

設定項目を一か所にまとめて書くのではなく、配列の中で分割して書いています。

```js
import js from "@eslint/js";
import typescriptEslintParser from "@typescript-eslint/parser";
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import onlyWarn from "eslint-plugin-only-warn";
import globals from "globals";

export default [
  // 設定項目1: lint対象ファイルの設定
  // fileのみ, ignoresのみの設定項目を作ることで、指定した内容が他の項目にも引き継がれる
  {
    // 例: JavaScriptとTypeScriptをlint対象にする
    files: ["**/*.{js,ts,mjs,mts,cjs,cts,jsx,tsx}"],
  },
  {
    // 例: distディレクトリはlint対象から外す
    ignores: ["**/dist/**"],
  },


  // 設定項目2: eslintの推奨ルールを使用する
  // 従来設定における"eslint:recommended"に相当。
  // import js from "@eslint/js"; した上で以下のように書く。
  js.configs.recommended,


  // 設定項目3: グローバル変数の設定
  // ここでは例としてNode.jsのグローバル変数を使用する。
  // import globals from "globals"; が必要。
  {
    // 特定のファイルのみこの設定を適用したい場合は、↓のように`files`を設定する
    // filesの指定が無ければ全ファイルに適用される
    // files: ["src/*.{js,ts,mjs,mts,cjs,cts,jsx,tsx}"],
    languageOptions: {
      globals: {
        ...globals.node, // Node.jsで動かすコードの場合
        // 他にも以下のような値が設定できる
        // ...globals.browser,
        // ...globals.worker
        // ...globals.jest
        // ...globals.jquery
        // ... etc.
      },
    },
  },


  // 設定項目4: TypeScriptパーサーの設定
  // 例: @typescript-eslint/parserを使用してTypeScriptのパースを行うよう設定する
  {
    languageOptions: {
      parser: typescriptEslintParser,
    },
  },


  // 設定項目5: プラグインの設定 (例: typescript-eslintの有効化)
  // どのルールを使用するか設定する
  {
    plugins: {
      "@typescript-eslint": typescriptEslint,
    },
    rules: {
      // プラグインから推奨ルールを使用したい場合はここに書く
      ...typescriptEslint.configs.recommended.rules,
      ...typescriptEslint.configs["recommended-type-checked"].rules,
      ...typescriptEslint.configs.strict.rules,
      ...typescriptEslint.configs["strict-type-checked"].rules,
      ...typescriptEslint.configs.stylistic.rules,
      // ルールの有効化/無効化を個別に設定することもできる
      "@typescript-eslint/no-misused-promises": "off",
      "@typescript-eslint/no-floating-promises": "off",
    },
  },


  // 設定項目6: 他のプラグインの設定
  // 例: 全errorをwarning扱いにするプラグインを使用する
  // 他のプラグインの設定も、こうして分割して配列に入れていけばよさそう
  {
    plugins: {
      "only-warn": onlyWarn,
    },
  },
];
```

いかがでしょうか？言語の設定やプラグインの設定項目を分割したことで、従来の書き方よりも見やすくなたように感じます。
特にプラグインの設定を分割して書くことで、

- 特定の拡張子だけにプラグインを適用する
- プラグインの設定を1か所にまとめて書く

のような事がやりやすくなりました。

# Flat Configのハマりポイント

記事の本題からは外れますが、Flat Configを導入するにあたりいくつか引っかかった点があるのでご紹介します。

### TypeScriptで型チェックすると型エラーが出る

FlatConfigはJavaScriptファイルですが、以下のようにJSDocを使用して型チェックすることができます。

```js
/** @type { import("eslint").Linter.FlatConfig[] } */
export default [
  ...
]
```

プラグインを使用している場合、Flat Configの型とプラグインの型が微妙にずれているため、型エラーが出る場合があります。

```
型 '({ files: string[]; ignores: string[]; } | { readonly rules: Readonly<RulesRecord>; } | { languageOptions: { globals: any; }; } | { languageOptions: { parser: typeof import("./node_modules/@typescript-eslint/parser/dist/index"); parserOptions: { ...; }; }; } | { ...; } | { ...; })[]' を型 'FlatConfig[]' に割り当てることはできません。
  型 '{ files: string[]; ignores: string[]; } | { readonly rules: Readonly<RulesRecord>; } | { languageOptions: { globals: any; }; } | { languageOptions: { parser: typeof import("./node_modules/@typescript-eslint/parser/dist/index"); parserOptions: { ...; }; }; } | { ...; } | { ...; }' を型 'FlatConfig' に割り当てることはできません。
    型 '{ languageOptions: { parser: typeof typescriptEslintParser; parserOptions: { EXPERIMENTAL_useProjectService: boolean; project: string; }; }; }' を型 'FlatConfig' に割り当てることはできません。
      'languageOptions.parser' の型は、これらの型同士で互換性がありません。
```

このような型エラーについては、プラグイン側でFlat Configに対応するまでの一時的なエラーだと思われるため、`@ts-ignore`で握りつぶすか`as unknown as`で正しい型にキャストすることで大勝しました。

### VSCode拡張でFlat Configを有効にする

ESLintのVSCode拡張では、Flat Configがデフォルトで有効になっていません。手動で設定して有効化する必要があります。

```diff_json:.vscode/settings.json
  {
+     "eslint.experimental.useFlatConfig": true
  }
```

# まとめ

- Flag ConfigはESLintの設定ファイルの新しい書き方
- 設定項目を分割できるので、設定の見通しが良くなる
- ただし書き方は今までと大幅に変わっているので注意

Flat Configはまだまだ新しい機能なのでネット上の情報が少ないですが、移行すると設定項目が管理しやすくなるので、みなさんぜひ移行していきましょう！