Menu

Строгий режим TypeScript: что включает strict: true

strict: true в tsconfig.json включает семейство проверок типов: noImplicitAny, strictNullChecks, strictPropertyInitialization и ещё пять. Что ловит каждая из них, как включить строгий режим в существующем проекте и какие полезные флаги в strict не входят.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Строгий режим это опция "strict": true в tsconfig.json. Это один переключатель для восьми флагов проверки типов, которые ловят неявный any, непроверенные null и undefined, небезопасное присвоение функций и неинициализированные поля классов. В TypeScript 7 strict включён по умолчанию. Вот как выглядит код, который проходит эту проверку:

Вывод: no user, SyntaxError и 42. Уберите : number, ?. или проверку instanceof, и файл перестанет компилироваться.

Как включить

{
    "compilerOptions": {
        "strict": true
    }
}

Отдельные флаги важнее strict в любую сторону. "strict": true, "strictNullChecks": false оставляет всё, кроме проверки null; "strict": false, "noImplicitAny": true включает только один этот флаг. strict также подключает проверки, которые появятся в будущих версиях, потому что новые флаги строгого семейства добавляются в группу.

strict в TypeScript не имеет отношения к директиве "use strict" из JavaScript, которая задаёт режим во время выполнения. TypeScript 7 всегда генерирует "use strict" там, где это нужно, а настройка alwaysStrict: false даёт ошибку TS5108 (опция удалена).

Что ловит каждый флаг

ФлагО чём сообщаетОшибка
noImplicitAnyпараметр или переменная, тип которых незаметно стал бы anyTS7006 Parameter 'x' implicitly has an 'any' type.
strictNullChecksиспользование значения, которое может быть null или undefinedTS18048 'u' is possibly 'undefined'.
strictFunctionTypesприсвоение функции, тип параметра которой уже требуемогоTS2322
strictBindCallApplyневерные аргументы для .call, .bind и .applyTS2345
strictPropertyInitializationполе класса, которому никогда не присваивается значениеTS2564 Property 'name' has no initializer and is not definitely assigned in the constructor.
noImplicitThisthis с неявным типом any, как во вложенной functionTS2683
useUnknownInCatchVariablesиспользование переменной catch до сужения (её тип unknown)TS18046 'err' is of type 'unknown'.
strictBuiltinIteratorReturnобращение к it.next().value встроенного итератора так, будто значение всегда определеноTS2322

Больше всего кода меняют noImplicitAny и strictNullChecks. Этот блок нарушает оба намеренно:

Он выводит index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type. и index.ts(11,13): error TS18048: 'user' is possibly 'undefined'. Без strict этот код компилируется, а затем падает во время выполнения с TypeError: Cannot read properties of undefined (reading 'name'). Со strict падение превращается в ошибку компиляции. Исправления показаны в первом блоке на этой странице.

strictFunctionTypes: зачем он нужен

Функцию, которая умеет работать только со строками, нельзя использовать там, куда могут прийти числа. Проверка ниже заглушена через @ts-expect-error, чтобы вы могли запустить код и увидеть, от чего защищает ошибка:

Без комментария присвоение даёт ошибку TS2322, Type '(s: string) => void' is not assignable to type 'Handler'., а следом Types of parameters 's' and 'value' are incompatible. Одно исключение оставлено намеренно: параметры методов, объявленных синтаксисом методов (handle(value: string | number): void внутри интерфейса), по-прежнему проверяются мягче, так что shout можно было бы присвоить такому методу без ошибки.

strictPropertyInitialization

Каждое поле класса должно получить значение в объявлении или в конструкторе. Три способа это выполнить:

Вывод:

Account {
  owner: 'Ada',
  balance: 0,
  history: [],
  lastLogin: undefined,
  sessionId: 's-1'
}

Свойства lastLogin и sessionId существуют со значением undefined, потому что при target ES2022 поля классов это настоящие поля JavaScript. ! убирает проверку, не добавляя никакой защиты во время выполнения, поэтому лучше использовать другие формы. Этому флагу нужен strictNullChecks; если его выключить, отключится и этот.

Как включить strict в существующем проекте

Если перевести большую кодовую базу на strict за один раз, могут появиться сотни ошибок. Поскольку в TypeScript 7 strict включён по умолчанию, обновление проекта, в tsconfig.json которого strict никогда не упоминался, само его включит; напишите "strict": false, если на время миграции нужно старое поведение. Путь, при котором сборка остаётся зелёной:

  1. Добавьте "strict": true и выключите флаги с наибольшим числом ошибок, обычно "strictNullChecks": false и "noImplicitAny": false.
  2. Исправьте оставшиеся ошибки, затем включите ещё один флаг и повторите.
  3. Для noImplicitAny большинство исправлений это аннотации параметров. Для strictNullChecks добавьте | undefined там, где значения могут отсутствовать, а затем обработайте это через ?., ?? или проверку if.
  4. Где исправление придётся отложить, поставьте на строку // @ts-expect-error с указанием причины. В отличие от @ts-ignore, он сообщает об ошибке, как только проблема исчезла, так что список сокращается сам.

Не заглушайте ошибки массово через as any или !: каждое такое место скрывает ровно ту ошибку, ради поиска которой добавлен флаг.

Полезные флаги, которых нет в strict

Они отдельные, потому что отвергают код, который часто корректен. Многие проекты всё равно их включают.

ФлагЧто делает
noUncheckedIndexedAccessarr[i] и record[key] включают undefined в свой тип
exactOptionalPropertyTypesdebug?: boolean допускает отсутствие ключа, но не debug: undefined
noImplicitReturnsкаждая ветка функции с возвращаемым значением должна что-то возвращать (TS7030)
noImplicitOverrideметод, переопределяющий метод базового класса, должен быть помечен override (TS4114)
noFallthroughCasesInSwitchнепустой case должен заканчиваться break, return или throw (TS7029)
noUnusedLocals, noUnusedParametersнеиспользуемые переменные и параметры считаются ошибками
noPropertyAccessFromIndexSignatureключи из индексной сигнатуры нужно читать через obj["key"], а не obj.key

Больше всего реальных ошибок ловит noUncheckedIndexedAccess. С ним:

const scores = [90, 85];
const d: Record<string, number> = {};

const third: number = scores[2]; // error TS2322: Type 'number | undefined' is not assignable to type 'number'.
const c: number = d["x"];        // same error

const safe = scores[2] ?? 0;     // number
for (const s of scores) {        // for...of is not affected: s is number
    console.log(s);
}

tsc --init в TypeScript 7 включает noUncheckedIndexedAccess и exactOptionalPropertyTypes в генерируемом конфиге рядом со strict.

Часто задаваемые вопросы

Что делает строгий режим в TypeScript?

"strict": true включает группу более строгих проверок: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables и strictBuiltinIteratorReturn. Вместе они не дают any появляться незаметно и делают null и undefined частью системы типов.

Включён ли строгий режим в TypeScript по умолчанию?

В TypeScript 7 да: strict по умолчанию равен true, так что проект без настройки strict получает все строгие проверки. tsc --init к тому же явно записывает "strict": true. Чтобы отказаться, нужно написать "strict": false.

Можно ли отключить одну строгую проверку, оставив остальные?

Да. Отдельные флаги переопределяют strict: { "strict": true, "strictNullChecks": false } оставляет все строгие проверки, кроме проверки null. Обычно именно так большую кодовую базу переводят по одному флагу за раз.

Строгий режим TypeScript это то же самое, что "use strict" в JavaScript?

Нет. "use strict" это режим JavaScript во время выполнения, который меняет поведение кода. strict в TypeScript меняет только то, о чём сообщает проверка типов. TypeScript 7 всегда генерирует "use strict" для немодульного вывода, а alwaysStrict: false теперь удалённая опция.

Входит ли noUncheckedIndexedAccess в strict?

Нет. noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitReturns, noImplicitOverride и noFallthroughCasesInSwitch это отдельные флаги, которые вы включаете сами. tsc --init включает первые два в генерируемом конфиге.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ