Правила, специфичные для TypeScript

Поддержка TypeScript в ESLint реализуется не на уровне ядра, а через экосистему специализированных пакетов, главным из которых является @typescript-eslint. Он расширяет базовую модель линтинга, добавляя понимание типов, синтаксиса и семантики TypeScript.

Ключевая идея интеграции заключается в том, что стандартный парсер ESLint не способен корректно анализировать TypeScript-код. Поэтому используется отдельный парсер, а поверх него — набор правил, учитывающих типовую систему.


Парсер TypeScript и его роль

Основой работы является @typescript-eslint/parser. Он заменяет стандартный парсер ESLint и преобразует TypeScript-код в AST, совместимый с ESLint.

Основные задачи парсера:

  • корректный разбор синтаксиса TypeScript (interfaces, enums, generics)
  • поддержка JSX при использовании React
  • передача информации о типах в правила линтера (при включённом type-aware режиме)

Пример конфигурации:

parser: '@typescript-eslint/parser',
parserOptions: {
  ecmaVersion: 2022,
  sourceType: 'module',
  project: './tsconfig.json'
}

Параметр project включает типо-зависимый анализ. Это важный режим, так как многие правила @typescript-eslint требуют доступа к TypeScript Compiler API.


Базовая конфигурация TypeScript-линтинга

Типовая структура конфигурации включает:

extends: [
  'eslint:recommended',
  'plugin:@typescript-eslint/recommended'
],
plugins: ['@typescript-eslint']

Набор recommended активирует правила, ориентированные на безопасность типов и предотвращение распространённых ошибок.


Категории TypeScript-правил

Правила делятся на несколько логических групп.


1. Правила, связанные с типами

Эта группа ориентирована на строгую типизацию и устранение слабых мест системы типов.

no-explicit-any

Запрещает использование any, которое разрушает типовую безопасность.

Пример проблемного кода:

function parse(data: any) {
  return data.value;
}

Альтернатива — использование unknown или дженериков.


no-inferrable-types

Удаляет избыточные аннотации типов, которые TypeScript способен вывести сам:

const count: number = 5;

Правильнее:

const count = 5;

explicit-function-return-type

Требует явного указания возвращаемого типа функции:

function sum(a: number, b: number): number {
  return a + b;
}

Это повышает предсказуемость API и предотвращает неявные изменения сигнатур.


2. Правила работы с импортами и модулями

TypeScript вводит дополнительные уровни импортов, которые требуют специальной обработки.


consistent-type-imports

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

import type { User } from './types';
import { createUser } from './service';

Преимущества:

  • улучшение tree-shaking
  • предотвращение циклических зависимостей
  • явное разделение контрактов и логики

no-duplicate-imports (TypeScript-aware)

Расширенная версия базового правила, учитывающая типовые импорты.


3. Правила интерфейсов и типов

TypeScript активно использует структурную типизацию, поэтому контроль за определениями типов критичен.


no-empty-interface

Запрещает пустые интерфейсы:

interface User {}

Такие конструкции не несут смысла и часто указывают на архитектурную ошибку.


interface-name-prefix (устаревающие практики)

Ранее использовались префиксы IUser, но современные конфигурации часто запрещают их для унификации:

interface IUser {} // нежелательно
interface User {}  // предпочтительно

member-delimiter-style

Контролирует стиль разделителей в интерфейсах и типах:

interface User {
  id: number;
  name: string;
}

Поддерживаются строгие кодстайл-политики для единообразия.


4. Правила классов и модификаторов

TypeScript расширяет классы за счёт модификаторов доступа и абстракций.


consistent-type-assertions-style

Контролирует стиль приведения типов:

const value = response as User;

или

const value = <User>response;

В современных конфигурациях предпочтителен as.


explicit-member-accessibility

Требует явного указания модификаторов:

class Service {
  public name: string;
  private cache: Map<string, string>;
}

Это улучшает читаемость и архитектурную прозрачность.


5. Правила безопасности типов

Эта категория ориентирована на предотвращение скрытых ошибок.


ban-types

Запрещает использование опасных или неоднозначных типов:

  • Object
  • Function
  • {} (в некоторых конфигурациях)

Пример:

let value: Object; // нежелательно

Рекомендуется использовать более точные типы.


no-unsafe-assignment (type-aware)

Запрещает присваивание значений с неявно небезопасным типом:

const value: string = getValue(); // если getValue(): any

no-unsafe-call

Контролирует вызовы функций с потенциально неизвестной сигнатурой.


6. Правила дженериков и сложных типов

TypeScript активно использует обобщения, что требует дополнительного контроля.


no-unnecessary-type-constraint

Запрещает избыточные ограничения:

function identity<T extends string>(arg: T): T {
  return arg;
}

Если ограничение не добавляет смысла, оно удаляется.


prefer-readonly

Рекомендует использование readonly для неизменяемых структур:

interface Config {
  readonly port: number;
}

7. Правила функций и сигнатур


typedef

Требует явного описания типов параметров и возвращаемых значений в определённых контекстах.

const add = (a: number, b: number): number => a + b;

no-misused-promises

Контролирует неправильное использование Promise в синхронных контекстах:

if (asyncFunction()) {
  // ошибка логики
}

8. Производительность и архитектура линтинга

Type-aware правила требуют подключения tsconfig.json, что влияет на производительность.

Ключевые особенности:

  • загрузка TypeScript Program API
  • анализ зависимостей проекта
  • построение графа типов

Для крупных проектов применяются оптимизации:

  • ограничение файлов через parserOptions.project
  • разделение конфигураций (base + node + test)
  • использование overrides

9. Конфигурационные слои TypeScript-правил

Типовая архитектура конфигурации включает несколько уровней:

  1. базовые правила ESLint
  2. TypeScript-правила
  3. доменные правила проекта
  4. переопределения для тестов и скриптов

Пример:

overrides: [
  {
    files: ['*.test.ts'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'off'
    }
  }
]

10. Типизация и переход от JavaScript к TypeScript-линтингу

Переход к TypeScript-правилам меняет модель анализа кода:

  • линтер перестаёт быть только синтаксическим инструментом
  • становится частично компиляторным анализатором
  • начинает учитывать семантику типов

Это приводит к более строгой, но более предсказуемой кодовой базе, где многие ошибки выявляются до выполнения программы.