Повышение качества JavaScript-кода с помощью eslint-plugin-unicorn
eslint-plugin-unicorn для ESLint 10: установка с flat config, пресеты recommended или unopinionated и autofixable-правила для повышения качества JS и TS.
eslint-plugin-unicorn — это крупный, принципиальный ESLint-плагин с более чем 300 правилами, который продвигает современные идиомы JavaScript и TypeScript, выявляет тонкие баги, влияющие на качество кода, и большинство его правил поддерживают автоматическое исправление. Плагин требует ESLint >=10.4, flat config и ESM, что означает: старая схема с .eslintrc и extends: 'plugin:unicorn/recommended' больше не работает. Если вы уже используете ESLint с базовой конфигурацией и хотите повысить качество кода, не создавая собственных правил, — этот плагин является самым быстрым способом добиться результата.
Загвоздка в том, что «принципиальность» — палка о двух концах. Одни правила предотвращают случайно квадратичные циклы и реальные баги корректности; другие навязывают стилистические предпочтения, которые будут конфликтовать с устоявшейся кодовой базой. Это руководство разграничивает их: что именно плагин контролирует, какие правила оправдывают себя, какие стоит понизить до предупреждений или отключить, а также готовый к копированию eslint.config.mjs, работающий с актуальным инструментарием.
Ключевые выводы
- eslint-plugin-unicorn поставляется с более чем 300 правилами, большинство из которых поддерживают автоматическое исправление, и начиная с версии v69 (июнь 2026 г.) требует ESLint ≥10.4, flat config и ESM —
.eslintrcбольше не работает, поскольку ESLint 10 полностью удалил эту систему. - Простейший путь к внедрению: расширьте конфигурацию через
unicorn/recommended, запуститеeslint --fix, затем отключите небольшое количество правил, конфликтующих с вашей кодовой базой — или начните с пресетаunopinionated, который заранее отключает спорные правила. - Плагин экспортирует три пресета:
recommended,unopinionated(recommended без спорных правил) иall. prefer-set-hasпревращает случайно квадратичные поиски через.includes()внутри цикла в O(n)-проверки сSet.has(), аno-array-for-eachпереписывает.forEach()вfor…ofдля более точного сужения типов в TypeScript.- Используйте плагин совместно с
@typescript-eslint/recommended-type-checked, чтобы объединить контроль идиом с типозависимым линтингом.
Что такое eslint-plugin-unicorn и как он устроен
eslint-plugin-unicorn — это набор правил ESLint, направляющих код к современным, менее подверженным ошибкам паттернам JavaScript и TypeScript: предпочтение Set перед сканированием массивов, for…of перед .forEach(), импорт с префиксом node: вместо голых имён модулей и так далее. Плагин требует ESLint >=10.4, flat config и ESM. Репозиторий декларирует более 300 правил, последняя версия — 69.0.0, релизы выходят примерно раз в месяц.
Два принципа дизайна определяют процесс внедрения. Во-первых, большинство правил поддерживают автоматическое исправление, поэтому значительная часть нарушений устраняется через eslint --fix, а не вручную. Во-вторых, плагин поставляется с готовыми пресетами, избавляя от необходимости включать правила по одному. Плагин экспортирует конфигурацию recommended, применяющую лучшие практики, и отдельно конфигурацию all, включающую все правила, кроме устаревших. В таблице правил используются эмодзи для обозначения статуса: ✅ — правило включено в конфигурацию recommended, ☑️ — в конфигурацию unopinionated, 🔧 — правило автоматически исправляется с помощью CLI-опции --fix.
Область применения охватывает преимущественно JavaScript и TypeScript, с одной полезной оговоркой: хотя большинство правил нацелены на JavaScript и TypeScript, некоторые также линтят файлы других типов при использовании соответствующего языкового плагина ESLint, такого как @eslint/css, @eslint/json, @eslint/markdown или @html-eslint/eslint-plugin. Для большинства команд именно правила для JS/TS являются основной причиной установки плагина.
Установка eslint-plugin-unicorn с flat config и ESM
Discover how at OpenReplay.com.
Установите плагин и совместимую версию ESLint, затем расширьте пресет recommended в flat-конфигурации eslint.config.mjs. Текущее требование — ESLint 10.x: ESLint 10 полностью удалил устаревшую систему eslintrc, оставив flat config единственным поддерживаемым форматом, поэтому любое руководство, демонстрирующее .eslintrc.json с extends: 'plugin:unicorn/recommended', устарело. ESLint требует актуальной версии Node.js (Node 20.19+, 22.13+ или 24+ согласно странице ESLint на npm).
npm install --save-dev eslint eslint-plugin-unicorn
Минимальная рабочая конфигурация, ограниченная файлами JavaScript и TypeScript:
// eslint.config.mjs
import unicorn from 'eslint-plugin-unicorn';
import { defineConfig } from 'eslint/config';
export default defineConfig([
{
files: ['**/*.{js,mjs,cjs,ts,tsx}'],
extends: [unicorn.configs.recommended],
rules: {
// переопределения здесь
},
},
]);
Это соответствует официальному примеру использования: импортируем unicorn из 'eslint-plugin-unicorn', импортируем {defineConfig} из 'eslint/config', затем внутри defineConfig задаём files и extends: [unicorn.configs.recommended]. Ограничение через files важно, когда вы линтите в одной конфигурации файлы разных языков — держите правила Unicorn для JS в отдельном объекте конфигурации.
Для выбора отправной точки доступны три пресета:
recommended— тщательно отобранный вариант по умолчанию.unopinionated—recommendedбез спорных правил.all— все неустаревшие правила.
Если ваша команда считает спорные правила слишком шумными, замените unicorn.configs.recommended на unicorn.configs.unopinionated и пропустите большую часть ручной настройки, описанной ниже.
Замечание по миграции: если вы всё ещё зависите от плагина, поставляющего только конфигурацию в стиле eslintrc, оберните его с помощью FlatCompat из @eslint/eslintrc. Используйте последнюю версию @eslint/eslintrc с ESLint 10.x.
Наиболее ценные правила eslint-plugin-unicorn: до и после
Наиболее ценные правила Unicorn стоит внедрять в первую очередь, поскольку каждое из них предотвращает проблему производительности, корректности или читаемости, а не просто навязывает стилистику. Все примеры ниже — результат работы eslint --fix, а не идеальный код, написанный вручную.
prefer-set-has — устраняет случайно квадратичные поиски
prefer-set-has помечает массивы, используемые исключительно для проверки принадлежности через .includes(), и направляет к использованию Set. Правило предпочитает Set#has() вместо Array#includes() при проверке наличия или отсутствия элемента. Это особенно важно внутри циклов: Array.prototype.includes имеет сложность O(n) на каждый вызов, поэтому вызов его на каждой итерации по другой коллекции даёт O(n²), тогда как Set.prototype.has работает за примерно константное время.
// ❌ O(n²): includes() заново сканирует весь массив на каждой итерации
const allowed = ['read', 'write', 'admin', /* …сотни значений… */];
const granted = requested.filter((scope) => allowed.includes(scope));
// ✅ O(n): Set.has() работает за примерно константное время на каждый поиск
const allowed = new Set(['read', 'write', 'admin', /* …сотни значений… */]);
const granted = requested.filter((scope) => allowed.has(scope));
Опасность этой проблемы в её незаметности: на малых данных всё работает нормально, а деградация производительности проявляется резко по мере роста объёма. Именно такие регрессии медленного взаимодействия вы потом диагностируете в продакшене, а не на этапе ревью.
no-array-for-each — лучшее сужение типов TypeScript и управление потоком выполнения
no-array-for-each переписывает колбэки .forEach() в циклы for…of. Правило предпочитает for…of методу forEach. Выгода не только стилистическая: for…of обеспечивает более точное сужение типов TypeScript на протяжении всего тела цикла, позволяет использовать await напрямую без акробатики с колбэками, а также поддерживает break/continue — ничего из этого не работает корректно внутри колбэка .forEach().
// ❌
users.forEach((user) => {
sendEmail(user);
});
// ✅ после автоисправления
for (const user of users) {
sendEmail(user);
}
prefer-node-protocol — однозначная идентификация встроенных модулей Node
prefer-node-protocol переписывает голые импорты встроенных модулей в явную форму с префиксом node:. Правило предпочитает использование протокола node: при импорте встроенных модулей Node.js. Явный префикс делает импорт однозначно идентифицируемым как встроенный модуль Node и исключает его случайное замещение одноимённым npm-пакетом.
// ❌
import fs from 'fs';
import { join } from 'path';
// ✅ после автоисправления
import fs from 'node:fs';
import { join } from 'node:path';
Ещё несколько полезных автоисправлений
Каждое из следующих правил сопоставляет устаревший паттерн с его современным аналогом и исправляет автоматически:
// throw-new-error — требует new при выбрасывании ошибки
throw Error('boom'); // ❌
throw new Error('boom'); // ✅
// prefer-string-replace-all — глобальная замена без регулярного выражения с /g
str.replace(/-/g, ' '); // ❌
str.replaceAll('-', ' '); // ✅
// prefer-array-flat-map — объединяет map().flat()
rows.map((r) => r.cells).flat(); // ❌
rows.flatMap((r) => r.cells); // ✅
// new-for-builtins — требует new для Map/Set/Array/Date и т.д.
const cache = Map(); // ❌
const cache = new Map(); // ✅
Правило throw-new-error требует new при создании ошибки (охватывая как const e = Error(), так и throw Error()), и оно включено в конфигурацию recommended и поддерживает автоисправление. new-for-builtins обязывает использовать new для всех встроенных объектов, кроме String, Number, Boolean, Symbol и BigInt. Array.prototype.flatMap одновременно нагляднее и избавляет от промежуточного массива, который создаёт .map().flat().
Два правила, ориентированных на читаемость, дополняют набор наиболее ценных. better-regex улучшает регулярные выражения, делая их короче и единообразнее (например, [0-9] заменяется на \d). prefer-ternary сворачивает простые блоки if/else: правило предпочитает тернарные выражения простым операторам if, возвращающим или присваивающим значения. Оператор if-else обычно занимает больше строк, чем однострочный тернарный, и может вынуждать объявлять переменные через let только ради их переприсваивания в блоках — оставляя их излишне изменяемыми и не позволяя prefer-const пометить переменную.
// ❌
function status(ok) {
if (ok) {
return 'pass';
} else {
return 'fail';
}
}
// ✅ после автоисправления
function status(ok) {
return ok ? 'pass' : 'fail';
}
Спорные правила и как принять решение
Некоторые правила из recommended навязывают предпочтения, а не обеспечивают корректность, и именно они создают наибольший шум в устоявшейся кодовой базе. В таблице ниже приведены рекомендации — оставить, понизить до предупреждения или отключить — для наиболее часто обсуждаемых правил, все из которых включены в recommended.
| Правило | Решение | Причина |
|---|---|---|
prevent-abbreviations | Warn или ограничить область | Переименовывает e → error, req → request, props → properties; конфликтует с именами, принятыми в вашем фреймворке и команде. |
no-null | Отключить при использовании null | Unicorn предпочитает undefined, но null вполне легитимен, когда его возвращают ORM или API — единственно верного ответа здесь нет. |
no-array-reduce | Оставить или понизить до warn | Запрещает Array#reduce() и Array#reduceRight(); разумно с точки зрения читаемости, но reduce вполне приемлем для многих команд. |
filename-case | Ограничить через ignore | Конфликтует с именами файлов, диктуемыми фреймворком; оставьте правило, но добавьте исключения. |
Случай с no-null наиболее очевиден: Unicorn предпочитает undefined, тогда как многие драйверы баз данных и HTTP API возвращают именно null, поэтому принудительное использование одного варианта создаёт лишние преобразования без какой-либо пользы для корректности. Отключите правило, если null является частью вашего контракта данных.
Отключать эти правила вручную не обязательно. Пресет unopinionated изначально оставляет спорные правила — включая no-null и prevent-abbreviations — выключенными, что делает его встроенным компромиссом между recommended и тщательно настроенной конфигурацией.
Для правил, которые вы оставляете, но хотите скорректировать, лучше ограничить область применения, а не отключать полностью. Типичный паттерн для filename-case — исключение файлов фреймворка:
{
files: ['**/*.{js,ts,tsx}'],
extends: [unicorn.configs.recommended],
rules: {
'unicorn/no-null': 'off',
'unicorn/prevent-abbreviations': 'warn',
'unicorn/filename-case': [
'error',
{
cases: { pascalCase: true, camelCase: true },
ignore: ['next-env\\.d\\.ts', 'vite\\.config\\.ts'],
},
],
},
}
Постепенное внедрение в существующую кодовую базу
Внедряйте плагин поэтапно, чтобы первый запуск не завалил вас ошибками. Рекомендуемая последовательность:
- Начните с пресета. Расширьте конфигурацию через
unicorn/recommendedили черезunicorn/unopinionated, если спорные правила слишком шумны для вашей команды. - Сначала запустите автоисправление. Выполните
npx eslint . --fix. Поскольку большинство правил поддерживают автоисправление, значительная часть нарушений исчезнет сразу, и вы проверите один механический diff вместо сотен встроенных ошибок. - Понизьте шумные правила до
warn. Всё, что продолжает срабатывать в большом объёме и не является проблемой корректности —prevent-abbreviationsобычно первый кандидат — переведите в'warn', чтобы не блокировать CI в процессе миграции. - Ограничивайте область через ignore, а не отключайте полностью. Используйте опцию
ignore(как в случае сfilename-case) и глобыfiles, чтобы задавать исключения, а не убивать полезное правило везде. - Добавьте типозависимый линтинг. Подключите
@typescript-eslint/recommended-type-checkedрядом с Unicorn. Unicorn контролирует идиомы; типозависимая конфигурация typescript-eslint выявляет баги, которые видна только системе типов. Они дополняют друг друга, и включение обоих — сильный выбор по умолчанию для TypeScript-проекта.
Относитесь к конфигурации как к живому документу. По мере обновления плагина — релизы выходят примерно раз в месяц — проверяйте таблицу правил на предмет новых правил и переименований, а также пересматривайте, какие правила в статусе warn готовы к повышению до error.
Самый быстрый путь к улучшению JavaScript с помощью eslint-plugin-unicorn прозаичен: расширьте конфигурацию через unicorn/recommended, запустите eslint --fix и уделите внимание лишь небольшому числу спорных правил, реально конфликтующих с принятым в команде стилем написания кода. Начните с пресета сегодня, дайте автоисправлению выполнить основную работу, а затем настраивайте по мере необходимости — официальная таблица правил является справочником по актуальному статусу и параметрам каждого правила.
Часто задаваемые вопросы
Совместим ли eslint-plugin-unicorn с typescript-eslint, или они конфликтуют?
Они дополняют друг друга и предназначены для совместного использования. Unicorn контролирует современные идиомы JavaScript и TypeScript, тогда как typescript-eslint добавляет типозависимые правила, выявляющие баги, которые видна только системе типов. Добавьте оба плагина как отдельные объекты конфигурации в вашем flat-файле eslint.config.mjs и используйте unicorn/recommended совместно с конфигурацией recommended-type-checked из typescript-eslint. Включение обоих — сильный выбор по умолчанию для TypeScript-проекта, без пересечений правил, требующих разрешения конфликтов.
В чём разница между пресетами recommended и unopinionated?
Пресет recommended — это тщательно отобранный вариант по умолчанию, включающий как правила корректности, так и спорные стилистические правила. Пресет unopinionated — это recommended без спорных правил: он заранее отключает такие противоречивые правила, как no-null и prevent-abbreviations, конфликтующие с устоявшимися кодовыми базами. Выбирайте unopinionated, когда стилистические правила кажутся вашей команде слишком шумными; это встроенный компромисс между recommended и тщательно настроенной конфигурацией. Третий пресет, all, включает все неустаревшие правила.
Почему моя старая конфигурация .eslintrc с extends plugin:unicorn/recommended перестала работать?
ESLint 10 полностью удалил устаревшую систему eslintrc, оставив flat config единственным поддерживаемым форматом, а eslint-plugin-unicorn требует ESLint 10.4 или выше, flat config и ESM. Старый синтаксис .eslintrc с extends 'plugin:unicorn/recommended' больше не загружается. Перейдите на eslint.config.mjs, который импортирует unicorn из 'eslint-plugin-unicorn' и задаёт extends как unicorn.configs.recommended внутри объекта конфигурации, ограниченного через files.
Не сломает ли добавление eslint-plugin-unicorn сборку сотнями ошибок?
Большинство нарушений устраняются автоматически, поскольку большинство правил unicorn поддерживают автоисправление. Сначала запустите eslint с флагом fix — это устранит значительную часть проблем в виде единого механического diff, а не сотен встроенных ошибок. Для правил, которые продолжают срабатывать в большом объёме, но не являются проблемами корректности — например, prevent-abbreviations — установите значение warn, чтобы не блокировать CI в процессе миграции. Такое поэтапное внедрение сохраняет работоспособность сборки в ходе освоения плагина.