0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js + TypeScript プロジェクトでESLint Flat Config に移行する手順とざっくりした内容のまとめ

Last updated at Posted at 2025-12-04

はじめに

筆者はNext.js + TypeScriptのプロジェクトに携わっていますが、どうやら.eslintrc.jsonからeslint.config.mjsへの移行が必要らしいということを知りました。

そこで今回移行した手順と、内容についてもざっくり記録してみましたので、よければ参考にしてみてください。


対象読者

  • eslintrc.jsonからeslint.config.mjsへの移行したい方
  • eslint.config.mjsの内容について理解を深めたい方

※ なぜ移行が必要?

参考記事:ESLint Flat Config に eslintrc から移行せねばならない


📝 この記事でできるようになること

  • 新しい ESLint の書き方「Flat Config(eslint.config.mjs)」がわかる
  • Next.js(React)と TypeScript のコードを正しくチェックできるようになる
  • 古い .eslintrc を使わずに、1つの設定ファイルで管理できるようになる
  • 自分のプロジェクトに合わせて ESLint の設定をカスタマイズできるようになる

📌 Step 1. 旧 .eslintrc.json を Flat Config に移行する

まずは migration ツールを使用します。

npx @eslint/migrate-config .eslintrc.json

※ zsh の場合は "..." で囲む必要あり:

npx @eslint/migrate-config .eslintrc.json

移行後、プロジェクトにeslint.config.mjsが生成されます。

↓このようなログが確認できれば成功しています。

Migrating .eslintrc.json
Wrote new config to ./eslint.config.mjs

以後、.eslintrc.jsonは不要になるため削除して問題ありません。

rm .eslintrc.json

📌 Step 2. ESM には __filename / __dirname が無いので再現する

Flat Config は ESM 形式(import/export) のため、

CommonJS のように __filename__dirname が使えません。

旧設定との互換用ツール FlatCompat は

「どのディレクトリを基準に設定を探すか」を必要とするため

以下を最初に書きます。

import { dirname } from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

✔ これでできること

  • eslint.config.mjs の“実際のディレクトリ”を基準に

    next/core-web-vitals などの extends を安全に解決できる


📌 Step 3. FlatCompat で旧 Next.js Recommended を読み込む

ESLint v9 以降、多くのプラグインが Flat Config 対応中です。

Next.js のルールも旧式で書かれているため、FlatCompat で読み込みます。

import { FlatCompat } from "@eslint/eslintrc";

const compat = new FlatCompat({
  baseDirectory: __dirname,
});

📌 Step 4. TypeScript の type-check ルールを有効化する

TypeScript の本気 lint は parserOptions.project が必要です。

languageOptions: {
  parser: typescriptParser,
  parserOptions: {
    project: true, // tsconfig.json を参照
  },
}

これにより、以下のような「型を理解した」ルールが機能します。

  • no-unnecessary-type-assertion
  • no-misused-promises
  • prefer-nullish-coalescing
  • no-floating-promises

こちらの具体例はひとつの記事にまとめましたので、よければご参照ください。
eslint.config.mjsのparserOptions.projectとは?


📌 Step 5. React コンポーネントの PascalCase を強制する

unicorn はファイル名制御に強力で、以下のように使います。

"unicorn/filename-case": [
  "error",
  {
    cases: {
      pascalCase: true, // React Component
      kebabCase: true,  // utils/hook/lib など
    },
  },
],

React コンポーネントは PascalCase が最も自然で読みやすく、

hooks や util は kebab-case が好まれるため、両方許容する形にしています。


📌 Step 6. import/no-default-export を“原則禁止・必要箇所のみ許可”にする

再利用性の観点から default export を禁止しつつ、

Next.js の page.tsx や layout.tsx で例外を作ります。

"import/no-default-export": "error",

↓ 例外

{
  files: [
    "**/page.tsx",
    "**/layout.tsx",
    "**/error.tsx",
    "next.config.*",
  ],
  rules: {
    "import/no-default-export": "off",
    "import/prefer-default-export": "error",
  },
}

📌 Step 7. 深すぎる相対 import を禁止し、@ エイリアスを推奨する

ただし相対パス完全禁止は Next.js と相性が悪いため、

安全性の範囲に調整した設定を採用します。

"no-restricted-imports": [
  "error",
  {
    patterns: [
      {
        group: ["../../*", "../../../*"],
        message:
          "階層が深い相対パスは @ エイリアスを使用してください。",
      },
    ],
  },
],

📌 Step 8. 完成した eslint.config.mjs

🔥 ESLint v9 + Next.js + TypeScript に最適化された、実務レベルの完全版です。

今回はあえてかなり丁寧にコメントアウトをつけています。

// ---------------------------------------------------------
// 基本ユーティリティ(ESMで__filename/__dirnameを再現)
// ---------------------------------------------------------
import { dirname } from "path";
import { fileURLToPath } from "url";

// ---------------------------------------------------------
// 旧 .eslintrc の設定を Flat Config に変換するための互換ツール
// Next.js は旧設定も使っていたため、移行補助として使用
// ---------------------------------------------------------
import { FlatCompat } from "@eslint/eslintrc";

// ---------------------------------------------------------
// TypeScript パーサー & TS専用ルールを提供するプラグイン
// TypeScript を正しく lint するために必須
// ---------------------------------------------------------
import typescript from "@typescript-eslint/eslint-plugin";
import typescriptParser from "@typescript-eslint/parser";

// ---------------------------------------------------------
// import 関連ルールを強化するプラグイン
// 例: default export 禁止、循環参照防止 など
// ---------------------------------------------------------
import importPlugin from "eslint-plugin-import";

// ---------------------------------------------------------
// コーディングスタイルを改善する強力なプラグイン
// 例: ファイル名規則、文字列の安全性、コード品質向上
// ---------------------------------------------------------
import unicorn from "eslint-plugin-unicorn";

// ---------------------------------------------------------
// Next.js 専用 ESLint ルール
// リンク、画像、ページ構造など Next.js 特有の問題を検出
// ---------------------------------------------------------
import nextPlugin from "@next/eslint-plugin-next";

// ---------------------------------------------------------
// ESM の現在ディレクトリを取得
// ---------------------------------------------------------
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// ---------------------------------------------------------
// 旧 .eslintrc 設定を Flat Config 形式へ変換するためのコンバータ
// ---------------------------------------------------------
const compat = new FlatCompat({
  baseDirectory: __dirname,
});

// ---------------------------------------------------------
// 📌 ここから ESLint 本体の設定
// ---------------------------------------------------------
export default [
  // -------------------------------------------------------
  // ① Next.js 推奨ルールセット(Flat Config 対応版)
  //    ・Core Web Vitals 準拠
  //    ・リンクや画像の誤用を検出
  //    ・Next.js 特有の非推奨パターンを防止
  // -------------------------------------------------------
  nextPlugin.configs.recommended,

  // -------------------------------------------------------
  // ② すべての JS/TS ファイルに適用される基本ルール
  //    ・行末の無駄なスペースだけ禁止
  //    → コード整形をきれいに保つための最低限ルール
  // -------------------------------------------------------
  {
    files: ["**/*.{js,jsx,ts,tsx,mjs,cjs}"],
    rules: {
      "no-trailing-spaces": ["error", { ignoreComments: true }],
    },
  },

  // -------------------------------------------------------
  // ③ TypeScript 専用ルールセット
  //    ・型チェックを利用した高度な lint が可能
  //    ・コードの安全性・保守性が最も向上する部分
  // -------------------------------------------------------
  {
    files: ["**/*.{ts,tsx}"],
    plugins: {
      "@typescript-eslint": typescript,
      import: importPlugin,
      unicorn: unicorn,
    },

    // TypeScript の構文解析設定
    languageOptions: {
      parser: typescriptParser,
      parserOptions: {
        project: true, // tsconfig.json の型情報を利用(高精度 lint)
      },
    },

    rules: {
      // TypeScript Recommended
      ...(typescript.configs["recommended-type-checked"]?.rules ?? {}),
      ...(typescript.configs["stylistic-type-checked"]?.rules ?? {}),

      // 配列型の書き方は自由(T[] / Array<T>)
      "@typescript-eslint/array-type": "off",

      // interface より type を統一的に使う
      "@typescript-eslint/consistent-type-definitions": ["error", "type"],

      // import type を必須化(型と値の混同を防ぐ)
      "@typescript-eslint/consistent-type-imports": [
        "warn",
        {
          prefer: "type-imports",
          fixStyle: "inline-type-imports",
        },
      ],

      // 未使用変数の警告(_始まりは許容)
      "@typescript-eslint/no-unused-vars": [
        "warn",
        { argsIgnorePattern: "^_" },
      ],

      // async 関数で await を強制しない
      "@typescript-eslint/require-await": "off",

      // React イベントハンドラで async が使いやすくなる設定
      "@typescript-eslint/no-misused-promises": [
        "error",
        {
          checksVoidReturn: { attributes: false },
        },
      ],

      // ---------------------------------------------------
      // 📌 ファイル名ルール
      //    ・コンポーネント → PascalCase
      //    ・その他 → kebab-case
      // ---------------------------------------------------
      "unicorn/filename-case": [
        "error",
        {
          cases: {
            pascalCase: true, // React コンポーネント
            kebabCase: true,  // hook/util/lib など
          },
        },
      ],

      // コールバックは基本 arrow function
      "prefer-arrow-callback": ["error", { allowNamedFunctions: true }],

      // default export は原則禁止(再利用性を高めるため)
      "import/no-default-export": "error",

      // 深い相対 import を禁止し、@ エイリアス利用を推奨
      // Next.js App Router に最適化して調整済み
      "no-restricted-imports": [
        "error",
        {
          patterns: [
            {
              group: ["../../*", "../../../*"],
              message:
                "階層が深すぎる相対パスは @ エイリアスを使用してください。",
            },
          ],
        },
      ],
    },
  },

  // -------------------------------------------------------
  // ④ テストファイル用の例外ルール
  //    ・any を許可
  //    ・未使用変数の制約を緩める(テストでは柔軟性が重要)
  // -------------------------------------------------------
  {
    files: [
      "**/__tests__/**/*.{ts,tsx}",
      "**/*.test.{ts,tsx}",
    ],
    rules: {
      "@typescript-eslint/no-explicit-any": "off",
      "@typescript-eslint/no-unused-vars": [
        "off",
        { varsIgnorePattern: "^_", argsIgnorePattern: "^_" },
      ],
    },
  },

  // -------------------------------------------------------
  // ⑤ エラーハンドリング専用ファイルの緩和設定
  //    ・意図的に unused にする変数を許可
  // -------------------------------------------------------
  {
    files: ["src/lib/errors.ts"],
    rules: {
      "@typescript-eslint/no-unused-vars": [
        "off",
        { argsIgnorePattern: "^_" },
      ],
    },
  },

  // -------------------------------------------------------
  // ⑥ Next.js が default export を必要とする特殊ファイル
  //    page.tsx / layout.tsx / config ファイル など
  //    → default export を許可する
  // -------------------------------------------------------
  {
    files: [
      "**/page.tsx",
      "**/layout.tsx",
      "**/error.tsx",
      "next.config.*",
      "postcss.config.*",
      "tailwind.config.*",
      "vitest.config.*",
    ],
    rules: {
      "import/no-default-export": "off",
      "import/prefer-default-export": "error",
    },
  },

  // -------------------------------------------------------
  // ⑦ lint を走らせないファイル・ディレクトリ
  // -------------------------------------------------------
  {
    ignores: [
      "*.md",
      "node_modules/",
      "dist/",
      ".next/",
    ],
  },
];

📌 Step 9. 動作確認

npm run lint

以下が出れば成功です。

✔ No ESLint warnings or errors

TypeScript の警告は無視してOK(プラグイン側の未対応問題です)。


まとめ:ESLint Flat Config は Next.js/TS と相性が良い

移行してみると、以下のメリットが強く感じられます。

✔ 設定が 1 ファイルに集約される

✔ import/export で自由にロジックを書ける

✔ プラグインの設定を柔軟に上書き可能

✔ TypeScript の type-check lint がより扱いやすい

✔ Next.js プロジェクト構造に合わせて綺麗に最適化できる

特に大型プロジェクトや複数人開発では、

Flat Config のメリットは非常に大きいです。


✍️ 終わりに

個人的には「Flat Configは難しい」という声をよく聞きますが、

本質は ESLint を“ただの JSON”から“JavaScriptのコード”として扱えるようにしただけ です。

つまり、

「JavaScriptを書ける人は ESLint をもっと自分のプロジェクトに最適化しやすくなる」

という非常に大きなメリットがあります。

あなたのプロジェクトでも間違いなくプラスに働くので、

少しずつ理解しながらカスタムしてみてください ✨


記事を読んでくださった方は、是非弊社開発課のXもフォローしてください。
毎日エンジニアに向けた情報発信を行っています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?