The TypeScript Strict Mode Survival Guide
Enabling strict mode in an existing TypeScript project can feel overwhelming. Here is a systematic approach to tackling each error category without losing your mind.
Why Strict Mode?
TypeScript's strict flag enables a bundle of checks: strictNullChecks, strictFunctionTypes, strictBindCallApply, noImplicitAny, and more. Together they eliminate entire categories of runtime errors at compile time.
Step 1: Enable One Flag at a Time
Rather than flipping "strict": true and drowning in errors, enable flags individually:
{
"compilerOptions": {
"strictNullChecks": true
}
}
Fix those errors, commit, then add the next flag.
Step 2: Tackle noImplicitAny First
noImplicitAny produces the most errors in a typical codebase but is also the most mechanical to fix. Use your editor's bulk-apply suggestions or a codemod like ts-migrate.
Step 3: Handle null and undefined
Once strictNullChecks is on, you will see errors wherever you assume a value is present. Common patterns:
// Before
function greet(name: string | undefined) {
return `Hello, ${name.toUpperCase()}`; // error!
}
// After: narrowing
function greet(name: string | undefined) {
if (!name) return "Hello, stranger";
return `Hello, ${name.toUpperCase()}`;
}
// After: non-null assertion (use sparingly)
function greet(name: string | undefined) {
return `Hello, ${name!.toUpperCase()}`;
}
Step 4: Use satisfies for Safer Inference
The satisfies operator (TypeScript 4.9+) lets you validate a value against a type while keeping a narrower inferred type:
const config = {
port: 8080,
host: "localhost"
} satisfies Partial<ServerConfig>;
config.port; // inferred as number, not number | undefined
Conclusion
Strict mode is an investment. The upfront cost pays dividends in refactoring confidence and reduced production incidents.