ESLint + Prettier + TypeScript が競合しない設定術
概要
ESLint・Prettier・TypeScript を同じプロジェクトに入れると、「保存のたびに整形が暴れる」「npm run lint と npm run format が逆方向の修正を出す」「eslint --fix が延々と差分を生む」といった競合が起きます。
本記事は、この競合の原因を切り分け、ESLint の Flat Config(eslint.config.js)で競合しない構成を作る手順をまとめたトラブルシュート記事です。結論は次の3点です。
- 整形は Prettier、コード品質は ESLint と責務を分ける
- ESLint の整形系ルールを
eslint-config-prettierで無効化する eslint-plugin-prettier(ESLint から Prettier を回す)は原則やめる
> なお ESLint は v10.0.0(2026-02)で従来の .eslintrc 設定システムが完全に削除されました。.eslintrc.* / .eslintignore や --no-eslintrc などは v10 では読まれず、設定は Flat Config のみが正となります。本記事も Flat Config を正として扱い、.eslintrc は v9 以前を使っている場合の参考に留めます。
環境
- Node.js 24.x(LTS)/ TypeScript 7.x
- ESLint 10.x(Flat Config)/ typescript-eslint 8.x
- Prettier 3.x / eslint-config-prettier 10.x
発生した問題
典型的な症状は3つです。
- 整形の往復: 保存のたびに ESLint の
--fixと Prettier の format が別々に走り、インデントやクォートが交互に書き換わる。 - lint と format が逆: ESLint は「セミコロンを消せ」、Prettier は「セミコロンを付けろ」。どちらを正としても片方が怒る。
eslint --fixが遅い・差分がうるさい:eslint-plugin-prettierを入れていると、Prettier の整形差分が ESLint エラーとして1行ずつ報告され、lint が重くなる。
原因
根本原因は、「整形」という同じ仕事を ESLint と Prettier の両方が担当していることです。
ESLint には indent / quotes / semi / comma-dangle など整形系ルールがあり、Prettier の整形と衝突します。さらに eslint-plugin-prettier を使うと、ESLint 実行中に Prettier を呼び出して「整形されていない箇所」をすべてエラー化するため、本質的なバグと整形差分がログ上で混ざります。
対応方法
方針は責務分離です。
| ツール | 担当 |
|---|---|
| Prettier | コードの整形(見た目) |
| ESLint | コード品質(バグの芽・アンチパターン) |
| TypeScript | 型チェック |
- ESLint の整形系ルールを
eslint-config-prettierで無効化する - 整形は
prettier --writeに一本化する eslint-plugin-prettierは原則使わない
実装例
インストール
npm i -D eslint prettier typescript \ typescript-eslint eslint-config-prettier
ESLint Flat Config(eslint.config.js)
ESLint v9 以降の標準であり、v10 では唯一の設定方式です。eslintConfigPrettier を配列の最後に置くことで、後勝ちで整形系ルールを無効化できます。
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier";
export default [
js.configs.recommended,
...tseslint.configs.recommended,
{
rules: {
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_" },
],
"no-console": "warn",
},
},
eslintConfigPrettier, // 必ず最後
];
従来の .eslintrc(v9 以前向け・v10 では利用不可)
ESLint v9 以前で .eslintrc を使っている場合は、extends の最後に "prettier" を置くのが鉄則です。ただし v10 では .eslintrc 自体が読まれなくなったため、この形式は使えません。v10 へ上げる場合は npx @eslint/migrate-config .eslintrc.json で Flat Config へ移行してください。
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
]
}
Prettier 設定(.prettierrc.json)
{
"semi": true,
"singleQuote": false,
"printWidth": 100,
"tabWidth": 2,
"trailingComma": "all"
}
npm scripts
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}
VSCode(.vscode/settings.json)
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
確認方法
競合ルールの残存チェック
npx eslint-config-prettier eslint.config.js
「No rules that are unnecessary or conflict with Prettier were found.」と出れば、ESLint 側に整形系ルールが残っていない証拠です。残っていればルール名が列挙されます。
整形後に差分が増えないこと
npm run format npm run lint git diff --stat # 整形由来の差分が出ないこと
注意点
eslintConfigPrettierは必ず最後に置く。途中に置くと後続設定が整形ルールを復活させ、競合が再発する。eslint-plugin-prettier(ESLint から Prettier 実行)とeslint-config-prettier(整形ルール無効化)は別物。本構成で必須なのは後者だけ。- CI は
lintとformat:checkを別ステップにすると、失敗理由が「バグ」か「整形」かで読み分けやすい。
まとめ
- 競合の原因は「整形を ESLint と Prettier が二重に担当する」こと
- 整形は Prettier、品質は ESLint、型は TypeScript と責務を分ける
eslint-config-prettierを設定の最後に置き、ESLint の整形ルールを無効化する- 整形は
prettier --writeに一本化し、npx eslint-config-prettierで残存競合を確認する
この3点を押さえるだけで、「保存のたびに差分が暴れる」問題はほぼ解消します。
