ESLint

Code QualityLinterJavaScriptTypeScriptDevOpsStatic AnalysisAuto-fixing

DevOps Tool

ESLint

Overview

ESLint is a code quality checking tool for JavaScript and TypeScript. It's the most popular linter that supports coding standard compliance, potential bug detection, and code style unification. With its customizable rule system and plugin ecosystem, it provides flexible configuration tailored to project needs. Auto-fixing capabilities and IDE integration significantly improve development efficiency, establishing it as the de facto standard tool for JavaScript development.

Details

ESLint was developed by Nicholas C. Zakas in 2013 and has now established itself as the de facto standard linter for JavaScript and TypeScript development. It uses static analysis to detect code issues, enabling coding standard compliance, early detection of potential bugs, and code style unification.

Key Features

  • Highly Customizable Rule System: Over 300 built-in rules and rich plugin ecosystem
  • Auto-fixing Capabilities: Automatically fixes many issues (--fix option)
  • Full TypeScript Support: Type-safe checking with @typescript-eslint/parser and plugins
  • Framework Support: Dedicated plugins for React, Vue.js, Angular, Astro, etc.
  • Modern JavaScript Support: Complete support for ES2024, JSX, and module systems
  • Flexible Configuration System: Support for both Flat Config (new format) and traditional .eslintrc format
  • IDE/Editor Integration: Integration plugins for VS Code, IntelliJ IDEA, Vim, etc.
  • CI/CD Integration: Automated quality checks in GitHub Actions, GitLab CI, etc.

As of 2025, it's standardly adopted in major projects like React, Next.js, Vue.js, Angular, and Node.js, achieving reduced code review burden and improved quality.

Pros and Cons

Pros

  • De facto standard for JavaScript/TypeScript development with abundant learning resources
  • Customizable rule system provides flexible adaptation to project requirements
  • Auto-fixing capabilities significantly reduce manual code correction work
  • Rich plugin ecosystem detects framework and library-specific issues
  • IDE integration provides real-time feedback, improving development efficiency
  • CI/CD integration enables automated checks as quality gates
  • Configuration file sharing enables team-wide coding standard unification
  • TypeScript support achieves both type safety and code quality

Cons

  • Complex initial setup (especially for large projects or TypeScript environments)
  • Many rules require learning cost for appropriate configuration selection
  • Plugin dependencies complicate configuration and increase maintenance burden
  • Long execution times in large projects
  • Confusion during transition period between Flat Config and traditional formats
  • Risk of reduced development speed due to overly strict configuration
  • Difficult to resolve plugin conflicts and configuration clashes

Reference Links

Code Examples

Installation and Basic Setup

# Install ESLint (project local)
npm install --save-dev eslint

# Install TypeScript-compatible ESLint
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

# Install with popular presets
npm install --save-dev eslint eslint-config-prettier prettier

# Run interactive configuration wizard
npx eslint --init

# Verify operation after configuration file creation
npx eslint src/**/*.js
npx eslint src/**/*.ts --ext .ts

Basic Configuration File (Flat Config Format)

// eslint.config.js - New Flat Config format (recommended)
import js from "@eslint/js";
import { defineConfig } from "eslint/config";

export default defineConfig([
    // JavaScript basic configuration
    js.configs.recommended,
    
    {
        files: ["**/*.js", "**/*.mjs"],
        languageOptions: {
            ecmaVersion: 2024,
            sourceType: "module",
        },
        rules: {
            // Error level settings
            "no-console": "warn",          // Console usage is warning
            "no-unused-vars": "error",     // Unused variables are errors
            "no-undef": "error",           // Undefined variables are errors
            
            // Style-related
            "semi": ["error", "always"],   // Semicolon required
            "quotes": ["error", "double"], // Force double quotes
            "indent": ["error", 4],        // 4-space indentation
            
            // Best practices
            "eqeqeq": "error",            // Force strict equality operators
            "curly": "error",             // Braces required
            "no-eval": "error",           // Prohibit eval
        }
    }
]);

TypeScript Configuration

// eslint.config.js - TypeScript support
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", // Reference TypeScript config file
            },
        },
        plugins: {
            "@typescript-eslint": tseslint,
        },
        rules: {
            // TypeScript recommended rules
            "@typescript-eslint/no-unused-vars": "error",
            "@typescript-eslint/no-explicit-any": "warn",
            "@typescript-eslint/prefer-const": "error",
            "@typescript-eslint/no-non-null-assertion": "warn",
            
            // Type safety enhancement
            "@typescript-eslint/strict-boolean-expressions": "error",
            "@typescript-eslint/prefer-nullish-coalescing": "error",
            "@typescript-eslint/prefer-optional-chain": "error",
            
            // Code style
            "@typescript-eslint/explicit-function-return-type": "warn",
            "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
            
            // Disable JavaScript basic rules (use TypeScript versions)
            "no-unused-vars": "off",      // Use TypeScript version
            "no-undef": "off",            // TypeScript compiler detects
        }
    }
]);

React + TypeScript Configuration

// 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", // Auto-detect
            },
        },
        rules: {
            // React basic rules
            "react/react-in-jsx-scope": "off",     // Not needed in React 17+
            "react/prop-types": "off",             // Type check with TypeScript
            "react/jsx-uses-react": "off",         // Not needed in React 17+
            "react/jsx-uses-vars": "error",        // JSX variable usage check
            
            // React Hooks
            "react-hooks/rules-of-hooks": "error",
            "react-hooks/exhaustive-deps": "warn",
            
            // JSX style
            "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",
        }
    }
];

Command Line Execution Patterns

# Basic execution
npx eslint src/

# Specific file execution
npx eslint src/components/Header.tsx

# Execution with auto-fix
npx eslint src/ --fix

# Specific extensions only
npx eslint src/ --ext .ts,.tsx

# Show only fixable issues
npx eslint src/ --fix-dry-run

# Specify custom configuration file
npx eslint src/ --config custom-eslint.config.js

# Detailed output formats
npx eslint src/ --format=json > eslint-results.json
npx eslint src/ --format=html > eslint-results.html

# Use cache (speed up)
npx eslint src/ --cache

# Apply only specific rules
npx eslint src/ --rule 'no-console: error'

# Configuration debugging
npx eslint --inspect-config
npx eslint --print-config src/index.ts

Inline Configuration and Rule Control

// Disable rules for entire file
/* eslint-disable no-console */
console.log("Console.log warnings disabled in this file");

// Disable rules for specific line only
console.log("Debug information"); // eslint-disable-line no-console

// Disable rules for next line only
// eslint-disable-next-line no-unused-vars
const unusedVariable = "This is not used";

// Disable multiple rules
/* eslint-disable no-console, no-alert */
console.log("Multiple rules disabled");
alert("Alert display");
/* eslint-enable no-console, no-alert */

// Temporary rule changes
/* eslint no-console: "error" */
// Console usage is error level here

// TypeScript-specific control
// Control with ESLint instead of @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const anyValue: any = "Type check disabled";

// React-specific control
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
    // Disable dependency array warning
}, []);

CI/CD Integration Example

# .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 script configuration example
{
  "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"
  }
}

Advanced Configuration and Customization

// eslint.config.js - Advanced configuration example
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,
    
    // Global configuration
    {
        languageOptions: {
            globals: {
                ...globals.browser,
                ...globals.node,
                ...globals.es2024,
            },
        },
        linterOptions: {
            reportUnusedDisableDirectives: "error",
        },
    },
    
    // TypeScript configuration
    {
        files: ["**/*.ts", "**/*.tsx"],
        plugins: {
            "@typescript-eslint": tseslint,
            "unicorn": unicorn,
            "perfectionist": perfectionist,
        },
        rules: {
            // Enforce high-quality code
            "unicorn/prefer-modern-math-apis": "error",
            "unicorn/prefer-node-protocol": "error",
            "unicorn/prefer-top-level-await": "error",
            
            // Sorting and organization
            "perfectionist/sort-imports": ["error", {
                "type": "natural",
                "order": "asc",
                "groups": [
                    "type",
                    "react",
                    "nanostores",
                    "internal-type",
                    "internal",
                    "external",
                    "unknown"
                ]
            }],
            
            // Performance
            "@typescript-eslint/prefer-readonly": "error",
            "@typescript-eslint/prefer-readonly-parameter-types": "warn",
            
            // Security
            "no-eval": "error",
            "no-implied-eval": "error",
            "no-new-func": "error",
        }
    },
    
    // Test file specific configuration
    {
        files: ["**/*.test.ts", "**/*.spec.ts", "**/__tests__/**/*"],
        rules: {
            "no-console": "off",               // Allow console usage in tests
            "@typescript-eslint/no-explicit-any": "off", // Allow any in tests
            "@typescript-eslint/no-non-null-assertion": "off", // Allow ! operator in tests
        }
    },
    
    // Configuration file specific
    {
        files: ["*.config.js", "*.config.ts"],
        rules: {
            "no-console": "off",
            "import/no-default-export": "off",
        }
    },
    
    // Ignore settings
    {
        ignores: [
            "dist/",
            "node_modules/",
            "coverage/",
            "*.min.js",
            "public/",
            ".next/",
            ".nuxt/",
        ]
    }
];

Custom Rule Creation Example

// custom-rules/enforce-foo-bar.js - Custom rule definition
module.exports = {
    meta: {
        type: "suggestion",
        docs: {
            description: "enforce foo variables to be equal to 'bar'",
            category: "Best Practices",
        },
        fixable: "code",  // Auto-fix support
        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 - Using custom rule
import myRule from "./custom-rules/enforce-foo-bar.js";

export default [
    {
        plugins: {
            "local": {
                rules: {
                    "enforce-foo-bar": myRule
                }
            }
        },
        rules: {
            "local/enforce-foo-bar": "error"
        }
    }
];