Современные инструменты статического анализа, построенные вокруг ESLint, давно вышли за пределы синтаксических проверок. Появление типизированного JavaScript через TypeScript привело к тому, что правила линтинга получили доступ к семантической информации: типам выражений, сигнатурам функций, структуре модулей и выводам компилятора.
Использование типовой информации внутри правил меняет саму модель анализа: вместо проверки «текста кода» происходит проверка «модели программы», построенной TypeScript-компилятором.
Типовая информация становится доступной только в том случае, если
ESLint работает через парсер, который умеет взаимодействовать с
TypeScript Compiler API. На практике это достигается через
@typescript-eslint/parser.
Ключевой механизм — parserServices, который передаёт в
правило:
program — экземпляр TypeScript ProgramesTreeNodeToTSNodeMap — соответствие между
ESTree-узлами и TypeScript-узламиtsNodeToESTreeNodeMap — обратное соответствиеgetTypeAtLocation(node) — получение типа выраженияИменно через program правила получают доступ к
TypeChecker, который формирует точные типы.
Основная точка входа — метод getTypeAtLocation.
Типовая схема работы:
Пример логики внутри правила:
const type = services.program.getTypeChecker().getTypeAtLocation(tsNode);
После получения типа правило может анализировать:
string, number,
boolean)Типы позволяют обнаруживать ошибки, которые невозможны в чистом JavaScript-анализе.
Например, обращение к свойству, отсутствующему в типе:
Useruser.passwordТакой анализ устойчив к переименованиям и рефакторингу.
Функции с перегрузками или union-типами невозможно корректно анализировать без типов.
Пример:
string | numberПравило может ветвить логику:
string → проверка строкиnumber → числовая логикаТипы позволяют распознавать структуры:
Promise<T>Array<T>Record<K, V>Это даёт возможность писать правила, ориентированные на семантику:
Promise без awaitArray.map callback return
typeRecordTypeChecker — центральный объект TypeScript API. Через него правила получают:
getSymbolAtLocation)Пример анализа функции:
Это позволяет реализовать сложные правила, например:
Типовая информация доступна только при включённой проектной конфигурации:
{
"parserOptions": {
"project": "./tsconfig.json"
}
}
Без этого TypeScript не строит полноценный Program, и
parserServices.program отсутствует.
Ограничения:
Правила ESLint делятся на два класса:
Работают без TypeScript:
Они:
Используют parserServices:
Они:
Одна из ключевых возможностей — проверка assignability.
Через TypeChecker:
Если типы несовместимы, правило фиксирует нарушение.
Примеры сценариев:
string в numberTypeScript поддерживает narrowing через:
typeofinstanceofПравила могут анализировать, корректно ли выполнено сужение типа:
Это позволяет находить:
Generics представляют отдельную сложность: типы становятся параметризованными.
Пример анализа:
identity<T>(value: T): TTВозможные проверки:
any вместо T)TypeScript-типы часто выступают формальной спецификацией API.
Правила ESLint могут использовать это для:
Если интерфейс описывает контракт, правило сравнивает:
Работа с типами требует значительных ресурсов, поэтому важна оптимизация:
getTypeAtLocationТипичные ошибки реализации правил:
TypeScript Program позволяет учитывать импортированные модули:
Правила могут анализировать:
anyТип any является точкой потери информации.
Правила могут:
anyTypeChecker способен вычислять тип сложных выражений:
Правила могут использовать это для:
Типовая информация особенно полезна в кастомных правилах.
Типовой шаблон:
context.parserServicesprogramcheckergetTypeAtLocationТакая структура позволяет создавать правила, которые:
Главное отличие типо-ориентированных правил — переход от поверхностной проверки к семантической модели:
Это делает возможным анализ, который ранее был доступен только компилятору, но теперь становится частью линтинга внутри ESLint.