はじめに
筆者は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-assertionno-misused-promisesprefer-nullish-coalescingno-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もフォローしてください。
毎日エンジニアに向けた情報発信を行っています。