Null reference errors: strictNullChecks forces you to handle null and undefined
Implicit any: Every variable must have a type or inferrable type
Unchecked indexing: Array/object access returns T | undefined
Optional property mutations: exactOptionalPropertyTypes distinguishes missing from undefined
Real bugs caught
The API response bug
// Without strict modeconst user = await fetchUser(id);console.log(user.name.toUpperCase()); // Runtime error if user is null// With strict modeconst user = await fetchUser(id);if (user) { console.log(user.name.toUpperCase()); // Safe}
The config merge bug
// Without strict modedefaultConfig.timeout = customConfig.timeout || defaultConfig.timeout;// If timeout is 0, it falls back to default. Bug.// With exactOptionalPropertyTypesif (customConfig.timeout !== undefined) { defaultConfig.timeout = customConfig.timeout;}
The array access bug
// Without noUncheckedIndexedAccessconst first = items[0];first.id; // Runtime error if array is empty// With the flagconst first = items[0];if (first) { first.id; // Safe}
The migration path
Migrating an existing codebase to strict mode is painful but systematic:
Enable strict: true in tsconfig
Run tsc --noEmit
Fix errors by file priority (API boundaries first)
Use satisfies operator for type-safe object literals
Add branded types for ID validation
We budget 2-3 days of engineering time per 10,000 lines of code.
The productivity argument
Critics say strict mode slows development. Our data says otherwise:
Bug escape rate: Down 60% after strict mode adoption
Code review time: Down 25% (fewer defensive checks needed)
Refactoring confidence: Up significantly (compiler catches renames and moves)
Onboarding time: Down (types document the codebase)
TypeScript strict mode is not a stylistic preference. It is a correctness tool. The time you save not debugging null reference errors in production pays for the initial migration cost within weeks.