39. Strict Mode
TypeScript: Броня. Урок 38: Strict Mode и Компиляторные Флаги
Заголовок раздела «TypeScript: Броня. Урок 38: Strict Mode и Компиляторные Флаги»Strict mode в TypeScript - это набор строгих проверок, которые делают код максимально type-safe. Понимание компиляторных флагов и правильная настройка tsconfig.json критически важны для production-ready проектов.
strict Флаг
Заголовок раздела «strict Флаг»{ "compilerOptions": { "strict": true // Включает ВСЕ строгие проверки }}"strict": true эквивалентен включению всех этих флагов:
strictNullChecksstrictFunctionTypesstrictBindCallApplystrictPropertyInitializationnoImplicitThisalwaysStrictnoImplicitAnyuseUnknownInCatchVariables
strictNullChecks
Заголовок раздела «strictNullChecks»Запрещает присваивание null и undefined к другим типам:
// strictNullChecks: truelet name: string = "Alice";// name = null; // ✗ Ошибка!// name = undefined; // ✗ Ошибка!
// Нужно явно указатьlet maybeName: string | null = "Bob";maybeName = null; // ✓
// Проверка перед использованиемfunction getLength(str: string | null): number { // return str.length; // ✗ Ошибка! str может быть null
if (str === null) { return 0; } return str.length; // ✓ TypeScript знает, что str не null}
// Optional chaininginterface User { profile?: { name?: string; };}
const user: User = {};const name = user.profile?.name?.toUpperCase(); // string | undefined
// Nullish coalescingconst displayName = user.profile?.name ?? 'Anonymous';strictFunctionTypes
Заголовок раздела «strictFunctionTypes»Обеспечивает правильную контравариантность параметров функций:
// strictFunctionTypes: trueinterface Animal { name: string;}
interface Dog extends Animal { bark(): void;}
// Без strictFunctionTypestype AnimalHandler = (animal: Animal) => void;type DogHandler = (dog: Dog) => void;
// const handler: AnimalHandler = (dog: Dog) => {}; // ✗ Ошибка с strictFunctionTypes
// Правильно - контравариантностьconst animalHandler: AnimalHandler = (animal: Animal) => { console.log(animal.name);};
const dogHandler: DogHandler = animalHandler; // ✓ Безопасно
// Методы в интерфейсах бивариантны (для совместимости)interface Handler { handle(animal: Animal): void;}
class DogOnlyHandler implements Handler { handle(dog: Dog): void { dog.bark(); // Работает, но небезопасно }}strictBindCallApply
Заголовок раздела «strictBindCallApply»Проверяет типы для bind, call и apply:
// strictBindCallApply: truefunction greet(name: string, age: number): string { return `Hello, ${name}! You are ${age} years old.`;}
// call - правильные типыgreet.call(null, "Alice", 30); // ✓// greet.call(null, "Alice", "30"); // ✗ Ошибка! "30" не number
// apply - правильные типыgreet.apply(null, ["Bob", 25]); // ✓// greet.apply(null, ["Bob"]); // ✗ Ошибка! Не хватает аргументов
// bind - правильные типыconst boundGreet = greet.bind(null, "Charlie");boundGreet(35); // ✓// boundGreet("35"); // ✗ Ошибка!strictPropertyInitialization
Заголовок раздела «strictPropertyInitialization»Требует инициализацию всех свойств класса:
// strictPropertyInitialization: trueclass User { name: string; // ✗ Ошибка! Не инициализировано email: string; age: number;
constructor(name: string, email: string) { this.name = name; this.email = email; // this.age не инициализирован - ошибка! }}
// Решения:
// 1. Инициализация в конструктореclass User1 { name: string; email: string; age: number;
constructor(name: string, email: string, age: number) { this.name = name; this.email = email; this.age = age; // ✓ }}
// 2. Значение по умолчаниюclass User2 { name: string; email: string; age: number = 0; // ✓ Значение по умолчанию
constructor(name: string, email: string) { this.name = name; this.email = email; }}
// 3. Optional propertyclass User3 { name: string; email: string; age?: number; // ✓ Optional
constructor(name: string, email: string) { this.name = name; this.email = email; }}
// 4. Definite assignment assertion (!)class User4 { name!: string; // ✓ "Я знаю, что инициализирую это позже" email!: string; age!: number;
constructor() { this.initialize(); }
private initialize() { this.name = "Default"; this.age = 0; }}noImplicitThis
Заголовок раздела «noImplicitThis»Запрещает неявный this тип any:
// noImplicitThis: trueinterface Point { x: number; y: number; moveBy(dx: number, dy: number): void;}
const point: Point = { x: 0, y: 0, moveBy(dx: number, dy: number) { // this.x += dx; // ✗ Ошибка! this имеет тип any },};
// Решение - явный тип thisconst point2: Point = { x: 0, y: 0, moveBy(this: Point, dx: number, dy: number) { this.x += dx; // ✓ this.y += dy; // ✓ },};
// В классах this выводится автоматическиclass Point3 { x = 0; y = 0;
moveBy(dx: number, dy: number) { this.x += dx; // ✓ this имеет тип Point3 this.y += dy; }}alwaysStrict
Заголовок раздела «alwaysStrict»Генерирует "use strict" в выходных JS файлах:
{ "compilerOptions": { "alwaysStrict": true }}// Сгенерированный JS"use strict";function example() { // ...}noImplicitAny
Заголовок раздела «noImplicitAny»Запрещает неявный тип any:
// noImplicitAny: true
// ✗ Ошибка! Параметр имеет неявный тип any// function log(message) {// console.log(message);// }
// ✓ Правильно - явный типfunction log(message: string): void { console.log(message);}
// ✓ Правильно - явный anyfunction logAny(message: any): void { console.log(message);}
// ✗ Ошибка в деструктуризации// function process({ name, age }) {// console.log(name, age);// }
// ✓ Правильноfunction process({ name, age }: { name: string; age: number }) { console.log(name, age);}useUnknownInCatchVariables
Заголовок раздела «useUnknownInCatchVariables»Тип unknown вместо any для переменных catch:
// useUnknownInCatchVariables: true (TS 4.4+)try { throw new Error("Something went wrong");} catch (error) { // error имеет тип unknown (не any)
// console.log(error.message); // ✗ Ошибка! unknown не имеет message
// ✓ Правильно - type guard if (error instanceof Error) { console.log(error.message); }
// ✓ Правильно - type assertion if (typeof error === 'object' && error !== null && 'message' in error) { console.log((error as Error).message); }}Практический tsconfig.json
Заголовок раздела «Практический tsconfig.json»{ "compilerOptions": { // Основные опции "target": "ES2020", "module": "commonjs", "lib": ["ES2020", "DOM"], "outDir": "./dist", "rootDir": "./src",
// Strict mode "strict": true,
// Дополнительные строгие проверки "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "exactOptionalPropertyTypes": true,
// Module resolution "moduleResolution": "node", "esModuleInterop": true, "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "isolatedModules": true,
// Source maps "sourceMap": true, "declaration": true, "declarationMap": true,
// Другое "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.spec.ts"]}Дополнительные Полезные Флаги
Заголовок раздела «Дополнительные Полезные Флаги»{ "compilerOptions": { // Неиспользуемые переменные "noUnusedLocals": true, "noUnusedParameters": true,
// Все пути должны возвращать значение "noImplicitReturns": true,
// Проверка fallthrough в switch "noFallthroughCasesInSwitch": true,
// Проверка индексного доступа "noUncheckedIndexedAccess": true,
// Требует override для переопределения "noImplicitOverride": true,
// Точная проверка optional properties "exactOptionalPropertyTypes": true }}Ключевые моменты
Заголовок раздела «Ключевые моменты»strict: trueвключает все строгие проверкиstrictNullChecksзапрещаетnull/undefinedбез явного указанияstrictFunctionTypesобеспечивает правильную вариантностьstrictBindCallApplyпроверяет типы дляbind/call/applystrictPropertyInitializationтребует инициализацию свойств классаnoImplicitThisзапрещает неявныйanyдляthisnoImplicitAnyзапрещает неявный типanyuseUnknownInCatchVariablesделаетcatchпеременныеunknown- Дополнительные флаги улучшают code quality
- Всегда используйте
strict: trueв новых проектах