ESLint — линтер для JavaScript

ESLint находит в коде ошибки, потенциальные баги и нарушения стиля до того, как они попадут на прод. Это статический анализатор: он не запускает код, а проверяет его по правилам, многие из которых можно исправить одной командой.

Уровень: средний 17 разделов ⏱ 2 часа

1. Что такое линтер и зачем он нужен

Линтер (linter) — программа, которая анализирует исходный код и находит проблемные места. ESLint — стандарт для JavaScript и TypeScript. Что он умеет:

ESLint — это «лишняя пара глаз» на каждом сохранении. В команде он особенно ценен: код ревью перестаёт быть спором о пробелах.

2. Установка

ESLint ставится как npm-пакет. Работай в папке проекта:

npm init -y
npm install --save-dev eslint
npx eslint --init   # интерактивная настройка

Убедись, что установка прошла:

npx eslint app.js
# пустой вывод = ошибок нет (или они ниже)

3. Flat config: eslint.config.js

С версии ESLint 9 используется flat config — файл eslint.config.js вместо старого .eslintrc. Минимальный конфиг:

// eslint.config.js
export default [
  {
    files: ["**/*.js"],
    rules: {
      "no-unused-vars": "warn",
      "semi": ["error", "always"]
    }
  }
]

Ключевые поля:

Часто путают: в «старой» схеме конфиг описывали через env и extends. В flat config их заменили languageOptions и объекты с configs.

4. Уровни правил

Каждое правило принимает одно из значений:

УровеньЗначениеЧто происходит
off0правило выключено
warn1жёлтое предупреждение, код работает
error2красная ошибка, «нельзя коммитить»
rules: {
  "no-undef": "error",        // запрет необъявленных переменных
  "quotes": ["error", "single"],  // одинарные кавычки
  "max-len": ["warn", { "code": 100 }] // строка до 100 символов
}

5. Готовые наборы: recommended и extends

Вместо написания ста правил с нуля подключают готовые наборы из объектов конфигурации линтера или отдельных пакетов:

// eslint.config.js с рекомендуемым набором
import eslint from "@eslint/js";

export default [
  eslint.configs.recommended,
  {
    files: ["**/*.js"],
    rules: {
      "no-unused-vars": "warn"
    }
  }
]

eslint.configs.recommended включает десятки проверенных правил: no-undef, no-unreachable, eqeqeq и другие. Большинство проектов начинают именно с него.

Для команд стоит сделать один «корпоративный» конфиг и подключать его во всех проектах: кода станет меньше повторов, а правила будут согласованы.

6. Автоисправление --fix

Многие правила исправляются автоматически (fixable). Команда:

npx eslint app.js --fix

ESLint переформатирует кавычки, точки с запятой, лишние пробелы и прочее. Например, правило quotes само заменит двойные кавычки на одинарные, если так настроено.

Не все правки помечены безопасными: правило всегда исправляет только то, что точно не меняет поведение. Остальное останется тебе на рассмотрение.

7. ESLint в VS Code

Установи расширение ESLint (от Microsoft). После этого ошибки подсвечиваются прямо в редакторе, а при включённой опции исправляются при сохранении:

{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}

Настройки VS Code уже знают путь ESLint из проекта; если используются нескольких окружений — укажи явно eslint.nodePath.

В панели Problems (Ctrl+Shift+M в VS Code) соберутся найденные линтером ошибки и предупреждения по всем открытым файлам.

8. Игнорирование файлов

Не все файлы нужно проверять: сборки, сторонние библиотеки, сгенерированный код. В flat config игнорирование — в поле ignores:

export default [
  {
    ignores: ["dist/**", "node_modules/**", "build/*.js", ".env"]
  },
  eslint.configs.recommended
]
Пока действует flat config, старый файл .eslintignore игнорируется — переноси всё в ignores конфига.

9. Плагины: React, Vue, TypeScript

Для фреймворков и TS подключаются отдельные плагины:

ПакетДля чего
eslint-plugin-reactправила для React: hooks, prop-types
eslint-plugin-vueправила для Vue SFC: template, script
typescript-eslintправила для TypeScript вместо расширения @typescript-eslint/*
eslint-plugin-importпроверка import/export
eslint-config-prettierотключает правила, конфликтующие с Prettier
npm install --save-dev typescript-eslint
// eslint.config.js
import tseslint from "typescript-eslint";

export default [
  ...tseslint.configs.recommended,
  { files: ["**/*.ts"] }
]
В проектах на Vue/React готовые наборы часто экспортируются как .configs[recommended] — проверь документацию плагина и разверни его через ....

10. ESLint в CI и git-hooks

Чтобы плохой код не попадал в основную ветку, линтер запускают автоматически: в CI на каждый push или pull request и локально перед коммитом через git hook. В GitHub Actions это шаг workflow:

// .github/workflows/ci.yml
steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with: { node-version: 20 }
  - run: npm ci
  - run: npx eslint . --max-warnings 0

Флаг --max-warnings 0 превращает предупреждения в ошибки: даже «жёлтые» проблемы остановят сборку и заставят их исправить, а не отложить.

Без линтера в CI команда защищена только дисциплиной. Обязательно настрой проверку перед мержем PR в GitHub — тогда баг с неиспользуемой переменной не доживёт до продакшена.
Для прекоммита удобен пакет lint-staged: он прогоняет линтер только по изменённым файлам, и проверка занимает секунды, а не минуты.

11. Связка ESLint и Prettier

ESLint отвечает за ошибки, но про форматирование спорить не умеет — для этого есть Prettier. Чтобы два инструмента не ругались друг с другом, подключают пакет eslint-config-prettier, отключающий конфликтующие правила:

npm install --save-dev prettier eslint-config-prettier

// eslint.config.js
import prettier from "eslint-config-prettier";

export default [
  eslint.configs.recommended,
  prettier
]

Разделение зон ответственности:

Запускай линтер после форматтера: сначала Prettier приводит текст к единому виду, потом ESLint вылавливает смысловые ошибки в уже аккуратном коде.
В VS Code включи «Format on save» для Prettier и «Fix on save» для ESLint — ревью перестанет спотыкаться о форматирование.

12. Отчёты и точечные отключения

Для CI полезно формировать отчёты в парсируемых форматах — например JSON или JUnit, по которым строят графики и ссылки в комментариях PR:

npx eslint . -f json -o eslint-report.json
npx eslint . -f junit -o junit.xml

В коде отдельные строки или файлы исключают точечно:

// отключить правило на следующую строку
// eslint-disable-next-line no-console
console.log("отладка");

// отключить для файла целиком
/* eslint-disable no-alert */
Точечные отключения — легальный приём, но злоупотреблять ими нельзя: десяток eslint-disable-next-line — сигнал пересмотреть правило или провести рефакторинг.
Пиши рядом с отключением комментарий-объяснение: через месяц и тебе, и команде станет ясно, почему правило молчит именно здесь.

13. Типовая настройка и чек-лист

Минимальный рабочий конфиг для старта на TypeScript без фреймворка:

import eslint from "@eslint/js";
import tseslint from "typescript-eslint";

export default [
  eslint.configs.recommended,
  ...tseslint.configs.recommended,
  { ignores: ["dist/**", "node_modules/**"] },
  {
    files: ["src/**/*.ts"],
    rules: {
      "@typescript-eslint/no-explicit-any": "warn",
      "no-console": "warn"
    }
  }
]

Короткий чек-лист для любого проекта:

Конфиг линтера — конвенция команды, а не личное мнение. Правь его через код-ревью, как обычный код, и обновляй при ревью новых правил.
В монорепозиториях держи общий конфиг в корне через пакет eslint-config и подключай его в каждом воркспейсе плоско.

14. Миграция на flat config и defineConfig

Переезд с .eslintrc на flat config — скользкое место. ESLint загружает конфиг как ESM, если в package.json стоит "type": "module", иначе как CommonJS. Чтобы не гадать, используют помощник defineConfig из пакета eslint/config или файл с явным расширением .mjs/.cjs:

// eslint.config.mjs — всегда ESM
import { globalIgnores } from "eslint/config";

export default [
  eslint.configs.recommended,
  globalIgnores(["dist/**", "node_modules/**"]),
  { files: ["**/*.js"], rules: { "no-unused-vars": "warn" } }
]

defineConfig добавляет автодополнение в IDE и проверку типов ключей. Для легаси-проектов есть пакет @eslint/eslintrc с FlatCompat — он переводит старые пресеты в flat-блоки, но убирай его при первой же возможности.

Не смешивай подходы: .eslintrc и flat config не работают вместе. ESLint 9 при наличии обоих файлов выбросит ошибку.

15. Линтер как аудит безопасности

ESLint незаменим там, где проверяется безопасность JS. Встроенные правила ловят опасные паттерны без сторонних пакетов: no-eval запрещает eval, no-implied-eval — косвенный вызов через setTimeout со строкой, no-new-func — new Function, no-unsafe-finally — риск потери исключения.

rules: {
  "no-eval": "error",
  "no-implied-eval": "error",
  "no-new-func": "error",
  "no-unsafe-finally": "error"
}

Плагины расширяют картину: eslint-plugin-security предупреждает о небезопасных регулярных выражениях и prototype pollution, для Node-проектов полезен eslint-plugin-security-node, а для React — проверка dangerouslySetInnerHTML через react/no-danger.

Линтер видит паттерны, а не данные. Для защиты от XSS и prototype pollution сочетай его со сканерами рантайма и ручным код-ревью.

16. Производительность и кэш

На крупных проектах полная проверка всех файлов занимает минуты. Ускоряют её тремя способами: кэш, точечный запуск и параллелизм.

npx eslint . --cache --cache-file .eslintcache --max-warnings 0

Файл .eslintcache добавь в .gitignore, чтобы не тянуть его в Git. Не перечисляй тяжёлые каталоги в правилах — исключай их заранее через ignores, тогда линтер вообще не откроет эти файлы. Если хочется ускорить сам движок — переходи на свежую мажорную версию: параллельная обработка правил заметно оживляет большие репозитории.

Замеряй: npx eslint . --debug показывает, какие файлы обрабатываются дольше всех и где кэш срабатывает.

17. Миграция с JSCS и TSLint

В истории JS было два глобальных «слияния» линтеров: JSCS влился в ESLint (2016), а TSLint официально уступил место typescript-eslint. Если в легаси-проекте остались старые конфиги, миграцию проводят так:

// было (TSLint)
"no-implicit-any": true
// стало (typescript-eslint)
"@typescript-eslint/no-explicit-any": "warn"
Не переноси правила «1 к 1»: синтаксис и поведение отличаются. После миграции прогони тесты — строгие правила TS часто вскрывают скрытые any и несоответствия типов.

Что дальше

Потренируй линтер на курсе JavaScript и TypeScript, а при сборке через Vite или Webpack запускай его в CI как один из шагов — тогда плохой код не попадёт в основную ветку GitHub. Про связку с тестами читай Vitest и Jest.

← Вернуться к курсам