Строгий режим и флаги компилятора
Зачем нужна строгость
TypeScript — это не переключатель «типы есть / типов нет». Это шкала строгости: чем выше вы её выкручиваете, тем больше ошибок находит компилятор.
По умолчанию TypeScript мягкий:
- Типы необязательны
nullиundefinedможно присвоить любому типу- Необъявленные типы становятся
any
Для нового проекта рекомендуется включать все строгие проверки сразу. Для миграции с JavaScript — включать постепенно.
Флаг strict
Флаг strict в tsconfig.json включает все строгие проверки одновременно:
{
"compilerOptions": {
"strict": true
}
}
Это эквивалентно включению каждого строгого флага по отдельности. Вы можете отключить отдельные проверки:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": false
}
}
noImplicitAny
Когда TypeScript не может определить тип, он использует any — тип, который отключает все проверки:
// Без noImplicitAny — нет ошибки, person: any
function greet(person) {
console.log("Hello, " + person.name);
}
greet(42); // Нет ошибки компиляции, но рантайм: "Hello, undefined"
С noImplicitAny TypeScript требует явную аннотацию:
// С noImplicitAny — ошибка: Parameter 'person' implicitly has an 'any' type
function greet(person) {
console.log("Hello, " + person.name);
}
Исправление:
function greet(person: { name: string }) {
console.log("Hello, " + person.name);
}
greet(42); // Ошибка: Argument of type 'number' is not assignable
anyделает TypeScript бесполезным — вы теряете проверку типов и подсказки в IDE. Используйтеunknownвместоany, если тип действительно неизвестен.
strictNullChecks
По умолчанию null и undefined можно присвоить любому типу:
// Без strictNullChecks
let name: string = null; // OK
let age: number = undefined; // OK
Это причина огромного числа багов. Тони Хоар, автор null, назвал его «ошибкой на миллиард долларов».
С strictNullChecks нужно обрабатывать null и undefined явно:
// С strictNullChecks
let name: string = null; // Ошибка: Type 'null' is not assignable to type 'string'
// Если null допустим — укажите это:
let name: string | null = null; // OK
Это заставляет обрабатывать пограничные случаи:
function getUser(): User | null {
// ...
}
const user = getUser();
// Ошибка: Object is possibly 'null'
console.log(user.name);
// Правильно — проверяем на null:
if (user) {
console.log(user.name); // OK
}
Другие строгие флаги
| Флаг | Что делает |
|---|---|
noImplicitAny | Запрещает неявный any |
strictNullChecks | null/undefined — отдельные типы |
strictFunctionTypes | Строгая проверка типов параметров функций |
strictBindCallApply | Строгая проверка bind, call, apply |
strictPropertyInitialization | Свойства класса должны быть инициализированы |
noImplicitThis | Запрещает this с неявным типом any |
alwaysStrict | Добавляет "use strict" в каждый файл |
useUnknownInCatchVariables | catch(e) — e имеет тип unknown, а не any |
tsconfig.json — основы
Вместо передачи флагов в командную строку используйте файл tsconfig.json:
{
"compilerOptions": {
"target": "es2020",
"module": "esnext",
"strict": true,
"noEmitOnError": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}
Создать шаблон:
tsc --init
Эта команда создаст tsconfig.json с комментариями ко всем опциям.
Подробный разбор всех опций — в главе про tsconfig.json.
Рекомендации
Для нового проекта
{
"compilerOptions": {
"strict": true,
"noEmitOnError": true
}
}
Включайте strict: true с самого начала. Исправить ошибки в новом коде проще, чем потом включать строгость в существующем.
Для миграции с JavaScript
{
"compilerOptions": {
"strict": false,
"noImplicitAny": false
}
}
Включайте флаги по одному: сначала strictNullChecks, потом noImplicitAny, и так далее.
Итоги
| Ситуация | Рекомендация |
|---|---|
| Новый проект | "strict": true |
| Миграция с JS | "strict": false, включать по одному |
| Обязательные два флага | noImplicitAny + strictNullChecks |
| Создать tsconfig | tsc --init |
strict: trueвключает все строгие проверки разомnoImplicitAnyзапрещает неявныйany— самый важный флагstrictNullChecksделаетnull/undefinedотдельными типами- Строгость — это шкала, а не переключатель