Engineering
Why TypeScript Is Still the Right Call in 2025
The debate was never really about types. It was about whether you trust your team to remember every decision they made six months ago.
Three years after TypeScript became mainstream, there is still a persistent faction of developers who treat it as optional - a preference, like tabs over spaces. Having shipped production systems in both TypeScript and plain JavaScript at scale, I want to make the case that TypeScript is not a style preference. It is a risk management decision.
The argument against is not wrong, it's just incomplete
The standard objection goes like this: TypeScript adds ceremony. You spend time writing types instead of writing features. In a fast-moving startup, that overhead is a competitive disadvantage.
This is a fair description of the short-term cost. It is also an incomplete description of the total cost.
What the argument omits is the category of bugs that only appear in large codebases maintained by multiple people over long time periods. These are not bugs introduced by carelessness. They are bugs introduced by context loss - by the gap between what a function was designed to do when it was written and what a caller assumes it does six months later.
TypeScript does not prevent you from writing bad code. It prevents a specific, high-cost category of mistake: the runtime surprise that emerges from a misread interface contract.
What we actually see in production
At HeapiFy, we have inherited enough codebases to have a clear empirical view of this. The pattern is consistent:
JavaScript codebases below approximately 20,000 lines are usually fine. The original author is still on the project, the surface area is small, and the mental model is intact.
Above that threshold, things diverge sharply. JavaScript codebases tend to accumulate a layer of defensive programming - if (thing && thing.prop && thing.prop.value) chains, any-shaped objects passed between functions, flag parameters that mean different things depending on call site. The code still works. It just becomes increasingly expensive to change without breaking something.
TypeScript codebases at the same size tend to be more legible because the contracts between modules are explicit. You can follow a data structure through ten layers of transformation and know, with confidence, what shape it has at each step. Refactoring is less frightening because the compiler surfaces breakages instead of hiding them until runtime.
The "you can always add types later" trap
One of the most expensive decisions we see is the decision to start in JavaScript and add TypeScript later. This almost never happens on the timeline that teams expect.
The reality is that migrating an active codebase to TypeScript means introducing types during a period when the codebase is still changing. You end up with a hybrid that has all the cost of TypeScript (build step, type definitions, tooling) and none of the benefit (because the any escape hatch is used liberally to unblock the migration).
The time to make the TypeScript decision is before you write the first line. The cost of starting with TypeScript is a few extra hours in week one. The cost of migrating to TypeScript is weeks of engineering time with a codebase in an unstable intermediate state.
What strict mode actually catches
If you are going to use TypeScript, use strict: true. The non-strict mode catches a subset of errors and gives you a false sense of coverage.
With strict: true enabled, the compiler catches:
- Implicit
any- forces you to be explicit about untyped values - Strict null checks - eliminates the billion-dollar mistake
- Strict function types - catches callback signature mismatches
- No implicit
this- prevents subtle context binding bugs
In our experience, strict mode eliminates roughly 30–40% of the integration bugs we used to find in code review and testing. These are not dramatic bugs. They are the small, tedious ones that are expensive precisely because they are hard to find manually and easy to prevent mechanically.
A practical recommendation
If you are starting a new project:
- Use TypeScript with
strict: truefrom day one - Define your domain types before you write implementation code - treat your
types/directory as documentation - Do not use
anyexcept at system boundaries (external API responses, JSON parsing output) and cast out of it immediately - Invest in good IDE integration - the feedback loop with TypeScript is only valuable if it is fast
The ceremony is real. The upside is not that TypeScript makes you a more productive developer in week one. It is that your team in week forty can still move at close to week-one velocity.
That is the actual return on investment.
Stay Current
Engineering notes, when we have something worth saying.
No weekly newsletters. No content marketing. When a post goes up, you'll get it.