Why TypeScript Strict Mode Should Be Non-Negotiable

Most projects enable TypeScript but leave strict mode off. Here's why that's a mistake that compounds over time, and what to do about it.

Most teams adopt TypeScript for the right reasons — better tooling, fewer runtime surprises, code that documents itself. Then they leave strict: false in the tsconfig and wonder why they’re still getting undefined is not an object in production.

Strict mode isn’t a pedantic setting. It’s the difference between TypeScript as a linter and TypeScript as a safety net.

What strict mode actually enables

Setting "strict": true in your tsconfig.json is shorthand for several compiler flags working together:

  • strictNullChecks — the big one. null and undefined are no longer assignable to every type. You have to handle them explicitly.
  • noImplicitAny — the compiler rejects variables whose type can’t be inferred. No more silent any spreading through your codebase.
  • strictFunctionTypes — function parameter types are checked contravariantly. Catches subtle callback mismatches.
  • strictPropertyInitialization — class properties must be initialised in the constructor or marked optional.

Individually these seem minor. Together they close the gaps that let bugs reach production.

The compounding problem

The real cost of skipping strict mode isn’t the bugs you see — it’s the ones you don’t. A non-strict codebase accumulates implicit any values that travel through your data flow silently. By the time one surfaces as a runtime error, it’s three function calls away from where it originated.

I’ve seen this pattern on inherited codebases repeatedly: TypeScript is installed, coverage looks high, but the type information is decorative. The types describe the happy path. Edge cases — null responses from an API, optional fields that aren’t present, user input that doesn’t match expectations — slip through because nothing forced the developer to handle them.

Enabling it on an existing codebase

Turning on strict mode in a mature codebase is uncomfortable the first time. The compiler will surface dozens — sometimes hundreds — of errors. This is not TypeScript being difficult. This is TypeScript showing you technical debt that existed but was invisible.

The practical approach:

  1. Enable "strict": true and run tsc --noEmit
  2. Fix errors file by file, starting with the ones on your critical path
  3. For genuine any escape hatches, use unknown and narrow explicitly rather than casting back to any
  4. Add "noUncheckedIndexedAccess": true once strict is clean — this catches array and object access patterns that strict alone misses

A new project should start strict from day one. There is no good reason not to.

The argument against is weak

The counterargument is usually velocity: strict mode slows you down. In my experience the opposite is true. The time you spend satisfying the type checker is orders of magnitude less than the time you’d spend debugging the runtime errors it prevents. Type errors at compile time take seconds to fix. Type errors in production take hours.

Strict mode is not a preference. It’s a professional standard.

Next step

Need an engineer who writes code this carefully?