ESLint
DevOpsツール
ESLint
概要
ESLintは、JavaScriptとTypeScriptのコード品質チェックツールです。コーディング規約の遵守、潜在的なバグの発見、コードスタイルの統一を支援する最も人気のあるリンター。カスタマイズ可能なルールシステムとプラグインエコシステムにより、プロジェクトのニーズに合わせた柔軟な設定が可能。自動修正機能とIDE統合により、開発効率を大幅に向上させ、JavaScript開発の事実上の標準ツールとして広く採用されています。
詳細
ESLint(Eslint)は2013年にNicholas C. Zakasによって開発され、現在はJavaScript・TypeScript開発における事実上の標準リンターとして確立されています。静的解析によってコードの問題を検出し、コーディング規約の遵守、潜在的なバグの早期発見、コードスタイルの統一を実現します。
主要な特徴
- 高度にカスタマイズ可能なルールシステム: 300以上の組み込みルールと豊富なプラグインエコシステム
- 自動修正機能: 多くの問題を自動的に修正(--fixオプション)
- TypeScript完全対応: @typescript-eslint/parserとプラグインによる型安全チェック
- フレームワーク対応: React、Vue.js、Angular、Astro等の専用プラグイン
- Modern JavaScript対応: ES2024、JSX、モジュールシステムの完全サポート
- 柔軟な設定システム: Flat Config(新形式)と従来の.eslintrc形式の両対応
- IDE・エディター統合: VS Code、IntelliJ IDEA、Vim等への統合プラグイン
- CI/CD統合: GitHub Actions、GitLab CI等での自動品質チェック
2025年現在、React、Next.js、Vue.js、Angular、Node.js等の主要プロジェクトで標準採用され、コードレビューの負荷軽減と品質向上を実現しています。
メリット・デメリット
メリット
- JavaScript/TypeScript開発の事実上の標準で豊富な学習リソース
- カスタマイズ可能なルールシステムでプロジェクト要件に柔軟対応
- 自動修正機能により手動でのコード修正作業を大幅削減
- 豊富なプラグインエコシステムでフレームワーク・ライブラリ固有の問題検出
- IDE統合によるリアルタイムフィードバックで開発効率向上
- CI/CD統合により品質ゲートとしての自動チェック実現
- 設定ファイル共有によるチーム全体でのコーディング規約統一
- TypeScript対応により型安全性とコード品質の両立
デメリット
- 初期設定の複雑さ(特に大規模プロジェクトやTypeScript環境)
- ルール数が多く適切な設定選択に学習コストが必要
- プラグイン依存による設定の複雑化とメンテナンス負荷
- 大規模プロジェクトでの実行時間が長い場合がある
- Flat Configと従来形式の移行期における混乱
- 過度な厳密設定による開発速度の低下リスク
- プラグイン間の競合や設定衝突の調整が困難な場合
参考ページ
書き方の例
インストールと基本セットアップ
# ESLintのインストール(プロジェクトローカル)
npm install --save-dev eslint
# TypeScript対応ESLintのインストール
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
# 人気のプリセット込みのインストール
npm install --save-dev eslint eslint-config-prettier prettier
# インタラクティブ設定ウィザード実行
npx eslint --init
# 設定ファイル作成後の動作確認
npx eslint src/**/*.js
npx eslint src/**/*.ts --ext .ts
基本的な設定ファイル(Flat Config形式)
// eslint.config.js - 新しいFlat Config形式(推奨)
import js from "@eslint/js";
import { defineConfig } from "eslint/config";
export default defineConfig([
// JavaScript基本設定
js.configs.recommended,
{
files: ["**/*.js", "**/*.mjs"],
languageOptions: {
ecmaVersion: 2024,
sourceType: "module",
},
rules: {
// エラーレベル設定
"no-console": "warn", // console使用は警告
"no-unused-vars": "error", // 未使用変数はエラー
"no-undef": "error", // 未定義変数はエラー
// スタイル関連
"semi": ["error", "always"], // セミコロン必須
"quotes": ["error", "double"], // ダブルクォート強制
"indent": ["error", 4], // インデント4スペース
// ベストプラクティス
"eqeqeq": "error", // 厳密等価演算子強制
"curly": "error", // 中括弧必須
"no-eval": "error", // eval禁止
}
}
]);
TypeScript設定
// eslint.config.js - TypeScript対応
import js from "@eslint/js";
import tseslint from "@typescript-eslint/eslint-plugin";
import tsparser from "@typescript-eslint/parser";
import { defineConfig } from "eslint/config";
export default defineConfig([
js.configs.recommended,
{
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 2024,
sourceType: "module",
project: "./tsconfig.json", // TypeScript設定ファイル参照
},
},
plugins: {
"@typescript-eslint": tseslint,
},
rules: {
// TypeScript推奨ルール
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/prefer-const": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
// 型安全性強化
"@typescript-eslint/strict-boolean-expressions": "error",
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/prefer-optional-chain": "error",
// コードスタイル
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/consistent-type-definitions": ["error", "interface"],
// JavaScript基本ルールの無効化(TypeScript版を使用)
"no-unused-vars": "off", // TypeScript版を使用
"no-undef": "off", // TypeScriptコンパイラが検出
}
}
]);
React + TypeScript設定
// eslint.config.js - React + TypeScript
import js from "@eslint/js";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "@typescript-eslint/eslint-plugin";
import tsparser from "@typescript-eslint/parser";
export default [
js.configs.recommended,
react.configs.flat.recommended,
{
files: ["**/*.tsx", "**/*.jsx"],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 2024,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
project: "./tsconfig.json",
},
},
plugins: {
"react": react,
"react-hooks": reactHooks,
"@typescript-eslint": tseslint,
},
settings: {
react: {
version: "detect", // 自動検出
},
},
rules: {
// React基本ルール
"react/react-in-jsx-scope": "off", // React 17+では不要
"react/prop-types": "off", // TypeScriptで型チェック
"react/jsx-uses-react": "off", // React 17+では不要
"react/jsx-uses-vars": "error", // JSX変数使用チェック
// React Hooks
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
// JSXスタイル
"react/jsx-quotes": ["error", "prefer-double"],
"react/jsx-indent": ["error", 4],
"react/jsx-indent-props": ["error", 4],
// TypeScript + React
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "warn",
}
}
];
コマンドライン実行パターン
# 基本的な実行
npx eslint src/
# 特定ファイルの実行
npx eslint src/components/Header.tsx
# 自動修正付き実行
npx eslint src/ --fix
# 特定の拡張子のみ
npx eslint src/ --ext .ts,.tsx
# 修正可能な問題のみ表示
npx eslint src/ --fix-dry-run
# カスタム設定ファイル指定
npx eslint src/ --config custom-eslint.config.js
# 詳細な出力形式
npx eslint src/ --format=json > eslint-results.json
npx eslint src/ --format=html > eslint-results.html
# キャッシュ使用(高速化)
npx eslint src/ --cache
# 特定ルールのみ適用
npx eslint src/ --rule 'no-console: error'
# 設定デバッグ
npx eslint --inspect-config
npx eslint --print-config src/index.ts
インライン設定とルール制御
// ファイル全体でルール無効化
/* eslint-disable no-console */
console.log("このファイルではconsole.log警告が無効");
// 特定行のみルール無効化
console.log("Debug information"); // eslint-disable-line no-console
// 次の行のみルール無効化
// eslint-disable-next-line no-unused-vars
const unusedVariable = "これは使われない";
// 複数ルールの無効化
/* eslint-disable no-console, no-alert */
console.log("複数ルール無効");
alert("アラート表示");
/* eslint-enable no-console, no-alert */
// ルールの一時的な変更
/* eslint no-console: "error" */
// ここではconsole使用がエラーレベル
// TypeScript固有の制御
// @ts-ignore の代わりにESLintで制御
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const anyValue: any = "型チェック無効";
// React固有の制御
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
// 依存配列の警告を無効化
}, []);
CI/CD統合例
# .github/workflows/lint.yml - GitHub Actions
name: ESLint Check
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: |
npx eslint src/ --format=json --output-file=eslint-results.json
npx eslint src/ --format=stylish
- name: Upload ESLint results
uses: actions/upload-artifact@v4
if: failure()
with:
name: eslint-results
path: eslint-results.json
# package.json のスクリプト設定例
{
"scripts": {
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"lint:check": "eslint src/ --max-warnings 0",
"lint:ci": "eslint src/ --format=json --output-file=reports/eslint.json"
}
}
高度な設定とカスタマイゼーション
// eslint.config.js - 高度な設定例
import js from "@eslint/js";
import globals from "globals";
import tseslint from "@typescript-eslint/eslint-plugin";
import unicorn from "eslint-plugin-unicorn";
import perfectionist from "eslint-plugin-perfectionist";
export default [
js.configs.recommended,
// グローバル設定
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
...globals.es2024,
},
},
linterOptions: {
reportUnusedDisableDirectives: "error",
},
},
// TypeScript設定
{
files: ["**/*.ts", "**/*.tsx"],
plugins: {
"@typescript-eslint": tseslint,
"unicorn": unicorn,
"perfectionist": perfectionist,
},
rules: {
// 高品質コード強制
"unicorn/prefer-modern-math-apis": "error",
"unicorn/prefer-node-protocol": "error",
"unicorn/prefer-top-level-await": "error",
// ソート・整理
"perfectionist/sort-imports": ["error", {
"type": "natural",
"order": "asc",
"groups": [
"type",
"react",
"nanostores",
"internal-type",
"internal",
"external",
"unknown"
]
}],
// パフォーマンス
"@typescript-eslint/prefer-readonly": "error",
"@typescript-eslint/prefer-readonly-parameter-types": "warn",
// セキュリティ
"no-eval": "error",
"no-implied-eval": "error",
"no-new-func": "error",
}
},
// テストファイル専用設定
{
files: ["**/*.test.ts", "**/*.spec.ts", "**/__tests__/**/*"],
rules: {
"no-console": "off", // テストでのconsole使用許可
"@typescript-eslint/no-explicit-any": "off", // テストでのany許可
"@typescript-eslint/no-non-null-assertion": "off", // テストでの!演算子許可
}
},
// 設定ファイル専用
{
files: ["*.config.js", "*.config.ts"],
rules: {
"no-console": "off",
"import/no-default-export": "off",
}
},
// 除外設定
{
ignores: [
"dist/",
"node_modules/",
"coverage/",
"*.min.js",
"public/",
".next/",
".nuxt/",
]
}
];
カスタムルール作成例
// custom-rules/enforce-foo-bar.js - カスタムルール定義
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "enforce foo variables to be equal to 'bar'",
category: "Best Practices",
},
fixable: "code", // 自動修正対応
schema: [],
},
create(context) {
return {
VariableDeclarator(node) {
if (node.id.name === "foo" &&
node.init &&
node.init.value !== "bar") {
context.report({
node,
message: "Variable 'foo' should be equal to 'bar'",
fix(fixer) {
return fixer.replaceText(node.init, '"bar"');
}
});
}
}
};
}
};
// eslint.config.js - カスタムルール使用
import myRule from "./custom-rules/enforce-foo-bar.js";
export default [
{
plugins: {
"local": {
rules: {
"enforce-foo-bar": myRule
}
}
},
rules: {
"local/enforce-foo-bar": "error"
}
}
];