12k
All articles

Линтинг TypeScript с помощью ESLint

Flat config ESLint 10 для TypeScript: настройка с typescript-eslint, typed linting через projectService и корректное подключение Prettier.

OpenReplay Team
OpenReplay Team
Линтинг TypeScript с помощью ESLint

На июль 2026 года актуальный способ линтить TypeScript — это ESLint 10 вместе с пакетом typescript-eslint во flat config (eslint.config.mjs), а не старая схема с .eslintrc, которую до сих пор показывает большинство результатов поиска.

Если вы когда-нибудь вставляли конфиг из туториала 2022 года и наблюдали, как ESLint полностью его игнорирует, — вот причина: он был написан для системы конфигурации, которой больше не существует. Замена короткая, хотя часть, отвечающая за проверки с учётом типов, требует одной дополнительной опции, которую легко пропустить.

ESLint 10 полностью удалил систему конфигурации eslintrc, о чём проект предупреждал в своих планах по внедрению flat config. Это единственное изменение ломает практически все туториалы, написанные до 2024 года, потому что ESLint больше вообще не читает файлы .eslintrc и .eslintignore. В этом руководстве вы получите корректный flat config для TypeScript, готовый к копированию, узнаете, как включить правила с учётом типов, и подключите линтинг к своим скриптам, редактору и CI.

Ключевые выводы

  • Современный стек — это ESLint 10 плюс typescript-eslint v8 во flat config; .eslintrc/.eslintignore мертвы начиная с ESLint 10.
  • Минимальный конфиг передаёт js.configs.recommended и tseslint.configs.recommended в defineConfig() из eslint/config, в файле с именем eslint.config.js/.mjs.
  • Правила с учётом типов вроде no-floating-promises требуют parserOptions: { projectService: true }. Пустой parserOptions их не включает.
  • Типизированный линтинг заставляет TypeScript собрать проект перед линтингом, поэтому он медленнее; запускайте его в CI, а в редакторе полагайтесь на кэширование IDE.
  • Во flat config флаг --ext устарел: выбор файлов задаётся glob-шаблоном files внутри каждого блока, так что скрипт линтинга — это просто eslint ..

Делают ли ESLint и TypeScript одну и ту же работу?

ESLint и TypeScript дополняют друг друга, а не конкурируют. Некоторые правила typescript-eslint действительно обращаются к системе проверки типов TypeScript для более глубокого анализа кода, но эти два инструмента отвечают на разные вопросы: компилятор TypeScript проверяет, что типы сходятся, а ESLint следит за стилем и отлавливает вероятные баги (неиспользуемые переменные, «висящие» промисы, небезопасные паттерны) по всей кодовой базе. Использовать нужно оба.

Если вы миграируете с TSLint, учтите: он мёртв уже много лет. Его создатели объявили в 2019 году о планах признать его устаревшим в пользу typescript-eslint, и экосистема ESLint стала стандартом для линтинга TypeScript. Нет никаких причин тянуть TSLint в новый проект.

Одно требование перед установкой: ESLint 10 отказался от поддержки старых версий Node. Теперь он работает на Node.js v20.19.0 и выше, v22.13.0 и выше или v24 и выше, а версии v21.x и v23.x больше не поддерживаются.

Как настроить ESLint для TypeScript?

Установите четыре пакета, которые вам действительно нужны:

npm i -D eslint @eslint/js typescript typescript-eslint

Вспомогательный пакет typescript-eslint включает в себя и парсер, и плагин, так что вам не придётся вручную подключать @typescript-eslint/parser и @typescript-eslint/eslint-plugin. Он поддерживает текущую мажорную версию: задокументированный диапазон версий ESLint для typescript-eslint охватывает ^8.57.0 || ^9.0.0 || ^10.0.0, поэтому typescript-eslint@latest (v8.x) работает с ESLint 10 без проблем.

Создайте eslint.config.mjs (flat config, а не .eslintrc):

// eslint.config.mjs
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';

export default defineConfig(
  js.configs.recommended,
  tseslint.configs.recommended,
);

Это рабочая база: рекомендованные правила ядра ESLint плюс рекомендованный набор typescript-eslint, который сам подключает парсер и плагин typescript-eslint. Функция defineConfig() входит в ядро ESLint, и сейчас именно ею следует пользоваться, поскольку typescript-eslint признал собственную tseslint.config() устаревшей в её пользу. Старый хелпер всё ещё работает, так что уже работающий конфиг не сломается, но новые проекты должны использовать defineConfig(). Импортировать tseslint нужно в любом случае — он по-прежнему требуется для tseslint.configs.* и вспомогательных функций для glob-шаблонов.

Ужесточаем правила, затем настраиваем их по отдельности

recommended — это точка отсчёта; два опциональных пресета поднимают планку. tseslint.configs.strict добавляет более категоричные правила корректности, а tseslint.configs.stylistic — правила согласованности, не требующие информации о типах. Добавьте их в массив конфигурации рядом с recommended.

Переопределить любое правило можно в блоке rules. Уровни серьёзности бывают трёх видов: off (или 0) полностью отключает правило, warn (или 1) сообщает о проблеме, не влияя на код выхода, а error (или 2) сообщает о ней и заставляет ESLint завершиться с кодом 1. Используйте warn для того, что хотите видеть, но не блокировать; используйте error для всего, что не должно попадать в репозиторий, поскольку это даёт ненулевой код выхода и роняет CI.

rules: {
  '@typescript-eslint/no-explicit-any': 'warn',
  '@typescript-eslint/no-unused-vars': 'error',
}

В современных конфигах предпочитайте строковые уровни серьёзности ('warn'/'error') числовой форме. Они читаются понятнее, а стиль с одними цифрами — характерный признак устаревших туториалов по .eslintrc.

Линтинг с учётом типов: правила, которым нужна информация о типах

Некоторым из самых полезных правил — в том числе no-floating-promises и no-misused-promises — нужна информация о типах, и включается она добавлением parserOptions: { projectService: true }. Это рекомендованный способ включить типизированный линтинг начиная с typescript-eslint v8; он заменил более старую опцию project, поскольку требует меньше настройки и работает быстрее. Также переключите пресеты на их варианты с проверкой типов (recommendedTypeChecked, strictTypeChecked, stylisticTypeChecked). Пустой parserOptions: {} не включает линтинг с учётом типов — распространённая ошибка в скопированных конфигах.

{
  files: ['**/*.ts', '**/*.tsx'],
  extends: [tseslint.configs.recommendedTypeChecked],
  languageOptions: {
    parserOptions: {
      projectService: true,
      tsconfigRootDir: import.meta.dirname,
    },
  },
}

Типизированный линтинг стоит вполне реальных ресурсов. Его включение означает, что TypeScript должен собрать ваш проект прежде, чем ESLint сможет его проверить: на небольшой кодовой базе это секунда-две, на крупной — заметно дольше. Собственная рекомендация typescript-eslint опирается на возникающую здесь асимметрию: плагины для редакторов кэшируют информацию о типах и в основном избегают этих издержек, поэтому полный типизированный линт стоит запускать в CI и на pre-commit, а в повседневной работе полагаться на редактор. Кроме того, projectService избавляет от старого костыля с поддержкой отдельного tsconfig.eslint.json, так как использует тот же проект, что и редактор.

Разделите JS и TS и настройте игнорирование

Правила с проверкой типов имеют смысл только для файлов, которые понимает TypeScript, поэтому ограничьте их областью **/*.ts/**/*.tsx и отключите для обычного JavaScript. У typescript-eslint есть пресет ровно для этого. Его официальная документация применяет tseslint.configs.disableTypeChecked к блоку **/*.js, чтобы снять специфичные для TypeScript настройки. Во flat config игнорируемые пути — это просто блок конфигурации с единственным ключом ignores, и именно он заменяет .eslintignore.

// eslint.config.mjs
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';

export default defineConfig(
  { ignores: ['dist/', 'node_modules/', 'coverage/', '**/*.d.ts'] },
  js.configs.recommended,
  {
    files: ['**/*.ts', '**/*.tsx'],
    extends: [tseslint.configs.recommendedTypeChecked],
    languageOptions: {
      parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
    },
    rules: { '@typescript-eslint/no-explicit-any': 'warn' },
  },
  { files: ['**/*.js', '**/*.mjs'], extends: [tseslint.configs.disableTypeChecked] },
  prettier, // must be last
);

Форматирование оставьте Prettier, затем настройте рабочий процесс

Держите форматирование вне ESLint. Добавьте eslint-config-prettier последним, чтобы отключить стилистические правила ESLint, конфликтующие с Prettier, и зафиксируйте версию ^10.1.8 или новее. Версия здесь важна: в июле 2025 года фишинговая атака на npm-учётные данные мейнтейнера привела к четырём скомпрометированным релизам, отслеживаемым как CVE-2025-54313. Версии 8.10.1, 9.1.1, 10.1.6 и 10.1.7 содержали postinstall-скрипт, который запускал встроенную DLL-нагрузку на Windows-машинах; исправленные релизы — 8.10.2, 9.1.2 и 10.1.8. Затронуты были только эти четыре версии, и нагрузка срабатывала лишь на Windows, поэтому чистые более ранние сборки, например 10.1.5, никогда не были скомпрометированы. Запуск Prettier как правила ESLint через eslint-plugin-prettier возможен, но необязателен; многие команды от этого отказываются, потому что линтинг становится медленнее и «шумнее».

Добавьте скрипт линтинга. Флаг --ext не нужен, поскольку выбор файлов задаётся glob-шаблоном files в каждом блоке конфигурации:

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix"
  }
}

Далее настройте запуск eslint --fix для файлов в staging-области через Husky и lint-staged перед каждым коммитом, включите автоисправление при сохранении в VS Code с помощью "source.fixAll.eslint": "explicit" в codeActionsOnSave и добавьте eslint . как шаг CI, чтобы упавшее правило блокировало merge.

И последнее, на что стоит обратить внимание: ESLint 9 достиг конца жизненного цикла 06.08.2026 и больше не получает обновлений. Если вы всё ещё на ESLint 9, приведённый выше конфиг работает на ESLint 10 без изменений, так что обновляйте среду выполнения и двигайтесь дальше. Начните с минимального конфига из двух строк, добавьте recommendedTypeChecked с projectService, когда вам понадобятся правила безопасной работы с промисами, и поставьте eslint-config-prettier последним.

Часто задаваемые вопросы

Стоит ли включать линтинг с учётом типов и чего это стоит?

Включайте его, если вам нужны самые полезные правила корректности вроде no-floating-promises и no-misused-promises, которые не могут работать без информации о типах. Цена в том, что ESLint заставляет TypeScript собрать проект перед линтингом: на небольших проектах это незначительно, на крупных — заметно. Большинство команд запускают полный типизированный линт в CI и на pre-commit, а в редакторе полагаются на кэширование IDE, где эти издержки не возникают.

В чём разница между projectService и project для типизированного линтинга?

Оба варианта включают типизированный линтинг, но typescript-eslint начиная с v8 рекомендует projectService — за простоту настройки и более быстрый линтинг, поскольку он переиспользует тот же tsconfig.json, который уже используется вашим редактором. Более старая опция project требует указывать путь к одному или нескольким файлам TSConfig и часто вынуждала команды поддерживать отдельный tsconfig.eslint.json. Используйте projectService: true, если у вас нет конкретной причины поступить иначе.

Работает ли флаг --ext в flat config ESLint?

Нет, во flat config --ext больше не нужен. Выбор файлов задаётся glob-шаблоном files внутри каждого блока конфигурации, например files: ['**/*.ts', '**/*.tsx'], так что ESLint уже знает, какие файлы проверять. Ваш скрипт линтинга превращается просто в eslint . без флага расширений. Скрипты, в которых всё ещё передаётся --ext, скопированы из туториалов эпохи до flat config, написанных для удалённой системы eslintrc.

Что использовать: eslint-config-prettier или eslint-plugin-prettier?

Для большинства проектов используйте eslint-config-prettier. Он отключает стилистические правила ESLint, конфликтующие с Prettier, и не добавляет накладных расходов во время выполнения; ставьте его последним в массиве конфигурации. Подход с eslint-plugin-prettier запускает Prettier как настоящее правило линтинга — это опционально и медленнее, а каждое расхождение в форматировании превращается в ошибку линтера. Зафиксируйте eslint-config-prettier на версии 10.1.8 или новее, чтобы не столкнуться с инцидентом в цепочке поставок от июля 2025 года.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.