Angular IDPStrong Junior
🔥 0

Модуль 2 · урок 14 з 15

Життя без any і ESLint

Останній пункт цілі звучить так: «налаштувати ESLint так, щоб будь-який any валив збірку». Це найпростіше в реалізації й найнезручніше в житті — саме тому воно й працює як навчальний прийом.

Чому any гірший за відсутність типів

const data: any = await res.json();

data.tasks.map(t => t.titel);   // друкарська помилка — компілятор мовчить
data.foo.bar.baz;               // такого немає — компілятор мовчить
const n: number = data.name;    // рядок у число — компілятор мовчить

any — це не «тип невідомий», а «перевірок не буде». Найгірше те, що він заразний: значення з any розтікається далі й вимикає перевірки в місцях, де типи були в порядку.

unknown — чесна альтернатива

const data: unknown = await res.json();

data.tasks;              // ❌ помилка: спершу доведи, що там є tasks

if (isTaskList(data)) {  // type guard із попереднього уроку
  data.forEach(t => t.title);   // ✅ тепер тип відомий
}

Різниця в одному: any каже «роби що хочеш», unknown — «спочатку переконайся». Обидва означають «тип невідомий», але лише другий змушує з цим щось зробити.

Налаштувати ESLint

ng add @angular/eslint
// eslint.config.js
export default tseslint.config(
  {
    files: ['**/*.ts'],
    extends: [
      eslint.configs.recommended,
      ...tseslint.configs.recommended,
      ...angular.configs.tsRecommended,
    ],
    rules: {
      // головне правило з таблиці IDP
      '@typescript-eslint/no-explicit-any': 'error',

      // корисні сусіди
      '@typescript-eslint/no-unnecessary-type-assertion': 'error',
      '@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
      '@typescript-eslint/explicit-function-return-type': ['warn', {
        allowExpressions: true,
      }],
      '@angular-eslint/prefer-on-push-component-change-detection': 'error',
    },
  },
);
ng lint            # перевірити
ng lint --fix      # виправити те, що виправляється автоматично
Правило про OnPush — безкоштовний бонус

@angular-eslint/prefer-on-push-component-change-detection закриває ще й вимогу з модуля 3: лінтер сам нагадає, якщо десь забув ChangeDetectionStrategy.OnPush. Одна строчка конфіга замість дисципліни.

Типові місця, де рука тягнеться до any

СитуаціяЗамість any
Відповідь HTTPhttp.get<Task[]>(url) — дженерик прямо в запиті
JSON.parseunknown + type guard
Подія в шаблоні($event.target as HTMLInputElement).value — конкретний тип, а не any
Обʼєкт із довільними ключамиRecord<string, string>
Функція, яка приймає що завгодноДженерик <T>
Помилка в catchВона й так unknown: перевір через instanceof Error
Стороння бібліотека без типівСвій .d.ts із мінімальним описом того, що реально використовуєш
// обробка помилки без any
try {
  await save();
} catch (e) {                       // e має тип unknown
  const message = e instanceof Error ? e.message : 'Невідома помилка';
  this.error.set(message);
}

Коли any все ж доводиться писати

// стороння бібліотека без типів, обгортаємо один раз
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- у legacy-віджета немає типів
declare const legacyWidget: any;

export function initWidget(el: HTMLElement): void {
  legacyWidget.mount(el);   // any лишився в одному рядку й не тече далі
}

Правило просте: якщо any неминучий — він має бути локальним і поясненим. Один рядок із коментарем «чому» — це нормальна інженерна практика. Десять eslint-disable по всьому проєкту — це вимкнений лінтер.

Лінтер у CI

# у пайплайні (модуль 9) перед тестами й збіркою
npm ci
npx ng lint
npx ng test --watch=false --browsers=ChromeHeadless
npx ng build --configuration production

Сенс не в тому, щоб карати за any, а в тому, щоб правило працювало без твоєї участі. Локально можна забути запустити лінтер — пайплайн не забуде. У модулі 9 ти навчишся читати його логи, коли він упаде.

Як пройти цю вимогу
  • Увімкнути правило одразу, а не наприкінці.
  • Кожен unknown супроводжувати type guard.
  • Дженерик у http.get<T> писати завжди.
  • Прогнати ng lint перед кожним комітом.
Як провалити
  • Лишити правило на рівні warn.
  • eslint-disable на весь файл.
  • Замінити any на object — те саме, тільки незручніше.
  • Вимкнути лінтер у CI, бо «падає».