Конфигурация и миграция — Примеры

Пример 1 — Проект, где аннотации только украшение

Компилятор запускается с выключенной строгой проверкой, и поле, допускающее null, падает в продакшене уже месяц. Скажите, что эта конфигурация на самом деле проверяет, затем включите проверку и заплатите её цену один раз.

{
  "compilerOptions": {
    "strict": false,
    "target": "ES2017"
  }
}
const city = order.customer.address.city;   // address может быть undefined; ошибки нет

Решение

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "noEmit": true
  },
  "include": ["src/**/*.ts"]
}
const city = order.customer.address.city;
                          ~~~~~~~
error TS18048: 'order.customer.address' is possibly 'undefined'.

С выключенным strict аннотации описывают замысел, и ничто его не проверяет, поэтому null и undefined проскакивают мимо всех проверок, а ошибки во время выполнения остаются на прежних местах. Включение превращает каждую из них в ошибку на строке, которая её вызвала, поэтому этот параметр стоит включать раньше остальных, а noEmit для фронтенд-проекта тоже обычно верен: код компилирует сборщик, а задача компилятора — только проверять его, чего он не сделает в конвейере, пока там не запущена соответствующая команда.

Пример 2 — Миграция, живущая три недели

Команда переводит весь репозиторий в одной ветке: конфликты накапливаются, работа останавливается, и ветку бросают. Скажите, почему подход провалился, затем переводите так, чтобы сборка оставалась зелёной.

{
  "compilerOptions": { "strict": true, "allowJs": false },
  "include": ["src/**/*"]   // все файлы сразу, все ошибки сразу
}

Решение

{
  "compilerOptions": { "strict": true, "allowJs": true, "checkJs": false, "noEmit": true },
  "include": ["src/api/**/*.ts", "src/models/**/*.ts"]
}
{ "scripts": { "typecheck": "tsc --noEmit", "check": "npm run typecheck && npm run test" } }

Перевод всего репозитория нельзя ни отревьюить, ни влить в основную ветку, поэтому он никогда и не доводится до конца; растущий список include переводит модуль за модулем, пока нетронутый JavaScript продолжает компилироваться. Начинать с общих границ — клиентов API и моделей данных — выгодно дважды, потому что их типы распространяются наружу и удешевляют перевод оставшихся файлов. allowJs без checkJs даёт импортам из нетипизированных файлов разрешаться без проверки их содержимого — это полезный промежуточный шаг, а не компромисс; проверка же должна стоять в конвейере, ведь ошибка, видимая только в редакторе того, кто последним запускал компилятор, — не принуждение.