The Fear of Strict Mode
Every TypeScript codebase I've joined has had at least one engineer who said "we'll enable strict mode later." Later never comes. I now enable it on line one of every project, and I'm going to show you why the upfront pain is worth it.
What Strict Mode Actually Does
// tsconfig.json
{
"compilerOptions": {
"strict": true
}
}
That single flag enables seven sub-options:
- ▸strictNullChecks —
nullandundefinedare not assignable to every type - ▸noImplicitAny — every binding must have an explicit type
- ▸strictFunctionTypes — prevents unsafe function type assignments
- ▸strictPropertyInitialization — class properties must be initialized
- ▸noImplicitThis — catches
thisscoping bugs in functions - ▸alwaysStrict — emits
"use strict"in every file - ▸strictBindCallApply — type-checks
bind,call,apply
The Bug It Caught in Production
On our Ensolite codebase, strict mode flagged this pattern during a PR:
// Without strict: compiled fine, crashed at runtime
function getOrderTotal(order: Order) {
return order.items.reduce((sum, item) => sum + item.price item.quantity, 0)
// ^ item.price could be undefined if the DB row had a NULL column
}
// With strictNullChecks: compile error → fix it before it ships
function getOrderTotal(order: Order) {
return order.items.reduce((sum, item) => {
const price = item.price ?? 0 // explicit null handling
return sum + price item.quantity
}, 0)
}
That exact pattern caused a NaN in an order total that slipped through a non-strict codebase at my previous job.
The Real Cost-Benefit
Upfront cost: Maybe 2–4 hours of fixing type errors on a new project, or a multi-day migration on a legacy one.
Ongoing benefit:
- ▸Entire null-deref class of bugs eliminated at compile time
- ▸Prisma and Zod schemas compose cleanly because the types actually match
- ▸Onboarding new engineers is faster — the types are the documentation
- ▸Refactors are safe — the compiler yells if you miss a callsite
If you're starting a new project today, there is no reason not to start with strict mode.