Строгий режим в TypeScript: включаем без остановки проекта
Флаг strict превращает молчаливые баги в ошибки компиляции, но включить его в проекте на 200 файлов одним махом нельзя. Разбираем, что делает каждый под-флаг и как мигрировать постепенно.

Один флаг strict в tsconfig.json одновременно включает семь проверок. На пустом проекте это делается за секунду. На проекте, который писали два года без строгого режима, включение strict обычно выдаёт от нескольких сотен до нескольких тысяч ошибок компиляции — и проект перестаёт собираться. Дальше разбираем, из чего состоит строгий режим в TypeScript 5.x, какая ошибка чем грозит и как включить его по частям, не останавливая разработку.
Что скрывается за одним флагом
"strict": true — это не отдельная проверка, а мета-флаг. Он выставляет в true группу опций. В TypeScript 5.x в неё входят:
strictNullChecks—nullиundefinedперестают быть частью любого типа;strictFunctionTypes— контравариантная проверка параметров функций;strictBindCallApply— типизацияbind,call,apply;strictPropertyInitialization— поля класса должны быть инициализированы;noImplicitAny— запрет неявногоany;noImplicitThis— запрет неявногоanyуthis;alwaysStrict— вставка"use strict"и парсинг в строгом режиме ECMAScript;useUnknownInCatchVariables— переменная вcatchполучает типunknown, а неany(входит вstrictначиная с TS 4.4).
Каждый из них можно выставить по отдельности. Именно на этом строится безопасная миграция: вы не включаете strict целиком, а зажигаете флаги по одному.
Строгий режим не находит новые баги. Он делает видимыми те, что уже лежат в коде: обращение к полю у потенциального undefined, забытую инициализацию, аргумент не того типа. Без него компилятор просто молчит.
Какая проверка чего стоит на практике
| Флаг | Что ловит | Сколько обычно ошибок |
|---|---|---|
noImplicitAny | параметры и переменные без типа, которые TS не смог вывести | средне, но чинится быстро |
strictNullChecks | доступ к свойствам у значений, которые могут быть null/undefined | больше всего, самая долгая часть |
strictPropertyInitialization | поля класса без значения в конструкторе | зависит от числа классов |
strictFunctionTypes | небезопасное присваивание колбэков | мало, но разбираться сложнее |
useUnknownInCatchVariables | обращение к error.message без проверки типа | по числу блоков catch |
Стратегия миграции: по флагам, а не всё сразу
Порядок построен от дешёвого к дорогому. Каждый шаг доводим до нуля ошибок и коммитим, прежде чем брать следующий флаг. Тогда в любой момент проект собирается и CI зелёный.
- Включите
noImplicitAny. Вtsconfig.jsonдобавьте"noImplicitAny": trueпри"strict": false. Соберите проект:npx tsc --noEmit. Ошибки вида Parameter 'x' implicitly has an 'any' type чинятся простановкой типа параметра. Это разгоняет вас на будущее: дальше компилятор перестанет молча пропускать нетипизированные места. - Добавьте
noImplicitThis,strictBindCallApply,alwaysStrict. Эти три обычно дают немного ошибок и включаются заодно. - Включите
strictNullChecks. Самый тяжёлый шаг. Здесь ошибок больше всего — компилятор перестаёт считать, что значение всегда есть. Не пытайтесь починить всё за один заход. - Включите
strictPropertyInitialization. Он работает только вместе соstrictNullChecks, поэтому идёт после него. - Замените набор флагов на
"strict": true. Когда все под-флаги зелёные, оставьте одинstrictи удалите остальные — так конфиг короче и вы автоматически получите новые проверки, которые Microsoft добавит вstrictв следующих версиях.
Как локализовать самый тяжёлый шаг
Со strictNullChecks проблема не в сложности правок, а в их количестве. Два рабочих приёма, чтобы не встать:
- Точечно, по файлам. В начало ещё не разобранного файла ставьте
// @ts-nocheck— компилятор пропустит его целиком. Снимаете комментарий по мере разбора. Минус — файл полностью выпадает из проверки, поэтому это временная мера, а не архитектурное решение. - Отдельный строгий конфиг для новых файлов. Заведите
tsconfig.strict.json, который наследует основной через"extends", включаетstrictNullChecksи в"include"перечисляет только уже мигрированные пути. CI гоняет оба конфига: обычный по всему коду, строгий — по мигрированной части. Новый код сразу пишется под строгими правилами.
Второй способ чище: он не портит проверку временными комментариями и гарантирует, что мигрированное не откатится назад.
// tsconfig.strict.json — TypeScript 5.x
{
"extends": "./tsconfig.json",
"compilerOptions": {
"strictNullChecks": true
},
"include": [
"src/core/**/*.ts",
"src/services/user.ts"
]
}
Проверка мигрированной части: npx tsc --noEmit -p tsconfig.strict.json. Пока команда возвращает ноль ошибок, откат в этих файлах не пройдёт незамеченным.
Типичные ошибки и как их чинить
Не подавляйте ошибки строгого режима вслепую. Каждая из них указывает на реальный сценарий, где значение может отсутствовать.
Object is possibly 'null' / 'undefined'
Самая частая ошибка после strictNullChecks. Возникает, когда вы обращаетесь к свойству значения, которое типизировано как T | null или T | undefined.
// document.querySelector возвращает Element | null
const input = document.querySelector('input');
// Правильно: сузить тип проверкой
if (input) {
input.value = '';
}
// Опциональная цепочка, если дальнейшая работа не обязательна
const len = input?.value.length;
Оператор ! (non-null assertion, input!.value) отключает проверку для конкретного места. Он говорит компилятору «здесь точно не null», но проверку в рантайме не добавляет. Используйте его, только когда гарантия действительно есть и не выражается типами, — иначе вы просто прячете тот баг, ради которого включали строгий режим.
Property has no initializer
Даёт strictPropertyInitialization на полях класса без значения.
class UserService {
// Ошибка: поле не инициализировано
// private client: HttpClient;
// Вариант 1: инициализация в конструкторе
constructor(private readonly client: HttpClient) {}
// Вариант 2: definite assignment, если значение
// проставляется извне (например, DI-фреймворком)
private handler!: () => void;
}
'error' is of type 'unknown'
С TS 4.4 переменная в catch имеет тип unknown. Обращаться к error.message напрямую нельзя — надо сузить тип.
try {
await saveOrder();
} catch (error) {
// Сужаем тип перед доступом к message
if (error instanceof Error) {
logger.error(error.message);
} else {
logger.error('Неизвестная ошибка', { error });
}
}
Чего строгий режим не делает
Флаги вне группы strict часто путают с ним, а они отдельные. noUncheckedIndexedAccess (доступ по индексу к массиву возвращает T | undefined), noImplicitReturns, noUnusedLocals, exactOptionalPropertyTypes — всё это в strict не входит и включается отдельно. Особенно полезен noUncheckedIndexedAccess: он ловит обращения к несуществующим элементам массива, что strictNullChecks сам по себе не покрывает. Но добавляйте его отдельным шагом уже после того, как основной strict зелёный, — иначе смешаете два фронта работ.
Инвалидация кеша инкрементальной сборки при смене конфига — отдельная тема; здесь важно лишь помнить, что после правки tsconfig.json стоит удалить *.tsbuildinfo, если проверка ведёт себя странно.
Что делать дальше
Когда "strict": true стоит и CI зелёный, закрепите результат: добавьте шаг tsc --noEmit в pre-commit или в pipeline, чтобы новая ошибка типов ломала сборку сразу. Следующий уровень — noUncheckedIndexedAccess и exactOptionalPropertyTypes: они строже, дают заметно больше ошибок и включаются так же по одному. Что именно входит в strict в вашей версии, сверяйте в разделе tsconfig reference на официальном сайте — набор под-флагов может пополняться между мажорными релизами.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: TSConfig Reference — strict и связанные флаги, TypeScript Handbook: Migrating from JavaScript
Частые вопросы
У меня TypeScript 4.x, а не 5.x — подход тот же?
Что делать, если ошибок после strictNullChecks несколько тысяч и это надолго?
Можно ли просто расставить оператор ! везде, где ругается компилятор?
Влияет ли strict на скомпилированный JavaScript?
А если после включения strict сборка стала медленнее?
Как заставить новый код писаться сразу под strict, пока старый мигрирует?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.