JS / TS

TypeScript Strict Mode: What Actually Happens When You Turn It On

3 min read

You flip "strict": true in an established codebase, the editor holds its breath, and the terminal delivers the verdict: 340 errors. This is the moment most strict-mode migrations die — someone declares it "not worth it right now," the flag gets reverted, and the codebase stays half-typed forever. Having shepherded this migration on a production app, I can tell you: the 340 number is misleading, the work is very doable, and the payoff is one of the best returns in maintenance engineering. Here's the map.

What strict mode actually checks

strict is an umbrella flag. The two that generate ~90% of your errors:

  • noImplicitAny — TypeScript will no longer silently type things as any when it can't infer them. Every unannotated function parameter surfaces. This is TypeScript admitting all the places it had quietly given up on checking your code.
  • strictNullChecks — the big one. null and undefined stop being assignable to everything. document.querySelector returns Element | null; users.find(...) returns User | undefined; and you must handle the empty case before using the value. This check alone targets the most common runtime crash in JavaScript: "cannot read properties of undefined."

The rest of the family (strictFunctionTypes, strictPropertyInitialization, strictBindCallApply, and friends) produce comparatively few errors and mostly catch genuinely wrong code.

The reframe that helps teams: strict mode doesn't create 340 problems — it reveals 340 places where problems were already possible. The bugs were in the building; you just turned the lights on.

Why incremental beats the big-bang PR

The 2,000-line "enable strict mode" PR is a trap: unreviewable, permanently in conflict with feature work, and rushed at the edges — which matters, because null-check fixes made carelessly (sprinkling ! everywhere) preserve the crashes while silencing the compiler. Incremental migration keeps every diff small and reviewed, and it never blocks the roadmap.

A file-by-file strategy that works

1. Stop the bleeding. Enable strict mode for the whole repo... in CI only for new files, or more simply: enable it and bulk-add a // @ts-nocheck (or targeted @ts-expect-error) header to currently-failing files via a script. New code is strict from day one; the legacy list only shrinks.

2. Burn down the list by traffic, not alphabet. Fix the highest-churn files first — shared utilities, API layers, anything imported everywhere. Fixes there propagate types into dependent files and often fix downstream errors for free. A stale admin screen can stay unchecked for months; nobody's touching it anyway.

3. Fix honestly, not cosmetically. The three tiers of null-handling, best to worst: actually handle the case (early return, error state); prove it can't happen and encode that (?? defaults, narrowing via type guards); and — rarely, with a comment — the non-null assertion !. If a file's fix is fifty !s, you haven't migrated it; you've disabled it with extra steps.

4. Ratchet in CI. Track the unchecked-file count and fail the build if it grows. Twenty minutes of scripting turns the migration from a project into a gradient — every sprint the number drops a little, and there's no way back.

The payoff, honestly stated

The short-term pain is real: a few weeks of visible burn-down work and some grumbling about | undefined. The long-term ledger is lopsided. After our migration, the "cannot read property of undefined" class of production error — previously our most common crash — essentially disappeared from new code. Refactors got faster and calmer: change a type, follow the compiler errors, done — no archaeology to find who else relied on the old shape. And code review improved in a subtle way: reviewers stopped spending attention on "what if this is null?" and spent it on logic.

Strict mode is TypeScript. Everything short of it is a preview with the safety features switched off — useful for adoption, but not the destination. Turn it on, ratchet the number down, and let the compiler carry the paranoia so your team doesn't have to.

Found this useful?

Share

I post shorter takes on javascript & typescript and frontend leadership on LinkedIn — follow along there, or get in touch if you're working on something related.