POST
TypeScript strict mode is a deal with your future self
Jul 20266 MIN READ
TypeScript has a setting called strict, and the way most projects end up is instructive. Somebody scaffolds the project, the template has strict enabled by default, and the codebase stays strict for its whole life. Another team starts a project, hits the first annoying type error, and turns strict off with a comment that will age like milk: "too noisy right now, will re-enable later." Later never comes. The project grows to fifty thousand lines of confident, unchecked JavaScript with a .ts extension.
I have worked in both kinds of codebase, and I would like to make the case that strict mode is not a linter preference. It is a contract with your future self, and the terms are simple. You pay a small amount of upfront friction, and the compiler enforces a set of rules that save you from a predictable set of disasters. The teams that take the deal get something they rarely notice: the absence of a whole class of bugs. The teams that skip it get to discover those bugs at the worst possible moments.
What strict actually turns on
Strict mode is not one setting. It is a bundle of compiler checks that remove the escape hatches TypeScript inherited from JavaScript. The famous ones are the interesting ones.
No implicit any. When you write a function parameter with no type, the compiler used to shrug and assume anything goes. Strict mode refuses. This is the check that feels pedantic on day one and prevents the worst bug on day two hundred: the refactor where a function you thought took a string was actually receiving an object, and every call site was wrong.
No null and undefined where you claim there is a value. Strict null checks mean a string can be undefined, and the compiler will make you prove otherwise before you use it. The bug this kills is the one that has taken down more services than I can count: the API response field that was sometimes missing, read with a confidence the data did not warrant.
No unchecked function calls on possibly empty values, no implicit index access on arrays that may be empty, no loose truthiness on non boolean conditions. Each check is small. Together they form a wall.
The deal is a trade of friction for certainty
The honest cost of strict mode is real. It is the type error that takes an hour to untangle. It is the generic that does not quite fit. It is the third party library with bad types that you have to wrap or declare. It is the occasional feeling, in week one, that the compiler is your enemy.
That feeling passes. What does not pass is the value on the other side of the trade. When you refactor in a strict codebase, the compiler is your partner, pointing at every call site that the change affects. When you touch code you have not seen in months, the types are a readable contract for what the function wants and promises. When you upgrade a dependency, the type errors tell you exactly what broke, instead of the runtime errors telling you after your users noticed.
The teams I have watched struggle with TypeScript are the ones that treat it as a badge to earn, who import libraries and then cast their way past every error, who write any to stop the noise. They get the worst of both worlds: the ceremony of types with none of the safety. The any escape hatch is the enemy, and strict mode exists mostly to make it loud every time someone reaches for it.
The real cost of the non-strict path
It is tempting to describe the non-strict path as "no friction", but that is wrong. The friction just moves somewhere else. It moves to the runtime, where it becomes a null reference in a production trace. It moves to the review, where the reviewer has to mentally execute every untyped function to catch what the compiler would have caught in the author's editor. It moves to the migration, which is the funniest part, because the cost is deferred, not avoided.
Here is the migration math that almost nobody does. The codebase has fifty thousand lines. The type checker, in non-strict mode, passes. You decide to go strict. The compiler now reports, say, two thousand errors. Most are implicit anys and missing null checks, mechanical, fixable in a week of grinding. But some are genuine, the ones where the types were lying, where the code was doing something the signature could not express. Those are the interesting ones, and they are the reason the migration is a gift.
A strict migration is a paid audit. It finds the places where your confidence was unjustified, and it does it before the bug finds you. The teams that do it late always say the same thing: "I wish we had done this years ago." The teams that never do it never say anything, because they never have a moment to compare against.
The discipline that makes it work
Strict mode is a wall, but walls have gates, and the discipline is in the gates. The any type is the gate, and it should be rare, visible, and commented. The as assertion is the other gate, and it should come with a justification, because an assertion is you telling the compiler that you know better. Sometimes you do. Often you do not, and the assertion hides it.
The teams that make strict work have a simple norm: type errors are fixed, not silenced. The escape hatches exist for the genuine edge, and the genuine edge is rarer than you think. If a developer finds themselves casting on every other line, that is not a TypeScript problem, it is a signal that the data model is fighting the code, and the fix is in the types, not around them.
The version that is actually sustainable
The sustainable version of strict is not maximal. It is enforced. The difference is the team that has every possible check on and the team that has strict on and a rule that the build fails when the lint rules flag the escape hatches. Enforcement is the part that matters, because a strict flag that nobody maintains is a strict flag that silently drifts.
The best teams also treat the types as documentation. They write the domain types first, the interfaces that describe the product, and the functions hang off them. The types become the readable spec of the system, and the compiler becomes the proofreader. That is the return on the week of friction: a codebase where the structure is visible, the refactors are safe, and the class of null-and-mistyped bugs has been priced out of existence.
Pay the week. Keep the wall. Future you will never know how many disasters you avoided, which is the point. The best safety systems are the ones whose value is measured in the incidents that never happen.