naoki.dev

記事検索

タイトル・本文・タグから検索します

ホームに戻る
フロントエンド

ESLintで「未使用importを保存時に自動削除」+「import順を自動整列」する(flat config)

目次

import の整理は地味に手間です。使わなくなった import が残る、並び順がバラバラになる——これを 保存(Ctrl+S)するだけで自動で 片付けたい。結論、必要なのは次の組み合わせです。

  • 未使用 import の自動削除eslint-plugin-unused-imports(削除フィクサー付き)
  • import 順の自動整列eslint-plugin-simple-import-sort
  • 保存時に発火 → VS Code の editor.codeActionsOnSavesource.fixAll.eslint
  • 整形は Prettier に一任(ESLint と役割分担)

ハマりどころは「@typescript-eslint/no-unused-vars は未使用 import を検出はするが自動削除できない」点。ここを差し替えるのが肝です。以下、ESLint flat config(v9)前提でまとめます。

なぜ no-unused-vars では消えないのか

TypeScript プロジェクトでよく使う @typescript-eslint/no-unused-vars は、未使用の変数・import をエラーとして報告してくれます。ただしこのルールには fixer(自動修正)が無いので、--fix や保存時アクションでは消えません。赤線は出るのに自分で消すしかない、という状態になりがちです。

そこで eslint-plugin-unused-imports を使います。こちらの no-unused-imports ルールは fixer 付きで、--fix(=保存時の source.fixAll.eslint)で該当 import 行を自動削除します。

インストール

pnpm add -D eslint-plugin-unused-imports eslint-plugin-simple-import-sort

simple-import-sort を既に入れているなら unused-imports だけでOK)

ESLint flat config

eslint.config.mjs にプラグインとルールを追加します。ポイントは @typescript-eslint/no-unused-vars を off にして、unused-imports 側に寄せること(両方有効だと二重報告になる)。

import simpleImportSort from "eslint-plugin-simple-import-sort";
import unusedImports from "eslint-plugin-unused-imports";
 
export default [
  // ...(next / typescript などの既存 config)
  {
    plugins: {
      "simple-import-sort": simpleImportSort,
      "unused-imports": unusedImports,
    },
    rules: {
      // import / export の並び順を自動整列(--fix で修正される)
      "simple-import-sort/imports": "error",
      "simple-import-sort/exports": "error",
 
      // 未使用 import は unused-imports で検出(フィクサー付き→保存時に自動削除)
      // 重複報告を避けるため TS 標準ルールは off にする
      "@typescript-eslint/no-unused-vars": "off",
      "unused-imports/no-unused-imports": "error",
 
      // 未使用「変数」はエラー(_ 始まりは意図的な未使用として許可)
      "unused-imports/no-unused-vars": [
        "error",
        { argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
      ],
    },
  },
];

unused-imports/no-unused-vars は、import ではない普通の未使用変数を拾う担当です。argsIgnorePattern / varsIgnorePattern_ 始まりだけ許可しておくと、意図的な未使用((_req, res) => ... など)を握りつぶせます。

VS Code:保存(Ctrl+S)で自動適用

ここまではルール定義。実際に保存時に走らせるには、.vscode/settings.jsoncodeActionsOnSave に ESLint の fixAll を指定します。

{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  }
}

これで保存すると 「未使用 import 削除 → import 並び替え → Prettier 整形」 が一括でかかります。

  • source.fixAll.eslint … ESLint の自動修正(unused-imports の削除+simple-import-sort の整列)
  • editor.formatOnSave + Prettier … 整形(インデント・引用符など)
  • 前提: VS Code に ESLint 拡張(dbaeumer.vscode-eslint)Prettier 拡張(esbenp.prettier-vscode) が入っていること

"source.fixAll.eslint" の値は "explicit"(保存時に実行)。旧来の true/false は非推奨で、"explicit" / "always" / "never" を使います。

モノレポで効かないとき

ルートに複数プロジェクトがある構成では、ESLint 拡張が flat config を見つけられず何も起きないことがあります。その場合は working directory を自動解決させます。

{
  "eslint.workingDirectories": [{ "mode": "auto" }]
}

Prettier と喧嘩させない

import の並び替えは simple-import-sort(ESLint)、コードの整形は Prettier、と役割を分けます。両者がバッティングしないよう、ESLint 側の整形系ルールは eslint-config-prettier で無効化しておくのが定石です(flat config なら末尾付近で読み込む)。

import prettier from "eslint-config-prettier/flat";
 
export default [
  // ...他の config
  prettier, // 整形系ルールを無効化。他より後ろに置く
];

import の並び順は Prettier の管轄外なので、simple-import-sort と競合しません。安心して併用できます。

動作確認

未使用 import と順序バラバラのファイルを用意して --fix すると、こうなります。

pnpm exec eslint --fix path/to/file.tsx
// before
import { useState } from "react"; // 未使用
import Image from "next/image";
import { QueryClient } from "@tanstack/react-query"; // 未使用
 
// after(未使用が消え、必要な import だけ残る)
import Image from "next/image";

CI でも同じルールで eslint--max-warnings=0 推奨)を走らせておけば、ローカルの保存時修正と CI の判定が完全に揃います。

まとめ

  • 未使用 import を消したいなら eslint-plugin-unused-imports@typescript-eslint/no-unused-vars は fixer が無いので off にして置き換える
  • import 順は eslint-plugin-simple-import-sort--fix で整列)
  • 保存時発火は VS Code の editor.codeActionsOnSave: { "source.fixAll.eslint": "explicit" }
  • 整形は Prettier に一任し、eslint-config-prettier を末尾で読み込んで競合を消す
  • CI でも同じ ESLint を回してローカルと揃える

参考ソース

関連記事