← Back to Blog
TypeScript A blue TypeScript cube and a yellow JavaScript cube sitting side by side on a platform, symbolizing the choice between static typing and a dynamic language.

TypeScript or a prayer before deploy? A case for static typing

Why dropping TypeScript for untyped JavaScript is a step backward: feedback, fearless refactoring, productivity, and the special case of Fastify.

📅 ✍️ Antoine Coulon
typescriptjavascriptstatic-typingrefactoringfastify

At a time when all the research and nearly every new programming language is moving toward ever more robust and stricter static compilers, we regularly see the opposite move emerge: teams getting their product talked about by stepping ten years back, abandoning a TypeScript codebase to start over on JavaScript without any types. Presented as a liberation, this choice deserves a closer look, because it amounts to voluntarily depriving yourself of one of the best tools we have today for producing quality software.

Because TypeScript, before being a matter of syntax or fashion, is first and foremost a feedback accelerator. A tool that fosters productivity, expressiveness, the explicit statement of intent, and code discovery. Why give that up? I still haven’t grasped the solid arguments raised against it, whether you use it directly or indirectly, for example via JSDoc (where, by the way, it’s TypeScript’s engine doing the work behind the scenes).

Static typing as a feedback loop

What matters most happens in the moment you write the code, not afterward. A static compiler tells you immediately that a call doesn’t match the expected signature, that a property doesn’t exist, that a case isn’t handled. This feedback arrives before you even run the program, before you write a single test, before any deployment.

It’s this immediacy that changes the very nature of the work. Rather than discovering an error at runtime (at best locally, at worst in production), you deal with it at the source, where the context is still fresh and the cost of fixing it is minimal. The compiler becomes a permanent copilot, marking out the terrain as you move forward.

The real test: a large-scale refactoring

If I had to keep only one argument, this would be it. A few days ago, I carried out a refactoring on a TypeScript codebase that touched more than a hundred files, and I wrapped it up in just a few hours. More importantly, I did it with complete peace of mind: the compiler flagged, file after file, exactly what was left to update, until everything was consistent.

I’d be curious to see anyone take on the same job on a pure JavaScript codebase, with the same confidence. Honestly, without TypeScript, I think I’d still be clenching my fists, grepping for function names, mentally tracing execution paths, hoping I hadn’t forgotten anything. Large-scale refactoring is precisely where the absence of types costs the most: every change becomes a gamble, whereas static typing turns it into a mechanical, verifiable operation.

”But unit tests are enough”

The objection comes up again and again: what’s the point of types, since unit tests already verify that everything goes fine at runtime? People then cite the good old expect(fn).toHaveBeenCalledWith(...), supposedly playing the role of a safeguard.

The problem is that you’re asking tests to do work that isn’t theirs. Verifying that a function receives the right type of argument is exactly the fundamental reason a compiler exists, so let it handle that task, which it does exhaustively and instantly, on every call, without you having to write anything. My unit tests, on the other hand, I prefer to focus where they bring value the compiler can’t provide: verifying that the system actually produces the expected business results.

Types and tests, then, aren’t in opposition: they split the work. The compiler guarantees structural consistency; the tests guarantee correctness of behavior. Making the latter carry the burden of the former condemns you to writing far more tests, for coverage that’s nonetheless less complete.

JavaScript isn’t the enemy

Let’s be clear: this isn’t about throwing JavaScript in the trash. There are perfectly legitimate use cases for pure JavaScript. The maintainer of an open source library who believes it makes them more productive and lowers their maintenance cost has every right to make that choice: it’s theirs, and they alone can weigh it with full knowledge of the trade-offs.

The Fastify case

The Fastify ecosystem is a good example: the entire core is written in JavaScript. But the story doesn’t end there. Fastify ships with declaration files (.d.ts) written by hand to enable adoption from TypeScript. And there, you can legitimately ask whether the maintenance cost isn’t, in the end, higher: it’s practically two joint codebases that have to be advanced in parallel and kept in sync.

The most telling signal comes from the creator himself. Matteo Collina, long a fervent advocate of full JavaScript, recently acknowledged publicly that his interest in TypeScript kept growing, and that he found real satisfaction in writing it. Coming from him, the admission is no small thing. And it’s worth remembering: Matteo Collina is not exactly your average JavaScript developer. If even the best, on projects designed around JavaScript, end up finding value in it, that says a lot about which way the wind is blowing.

Conclusion

For an isolated project, mastered end to end by a handful of experts, the debate can stay open. But on a codebase that’s growing, with several developers of varying skill levels actively working on it, with a demand for quality and the will to eliminate an entire class of bugs before execution even begins, there’s really, in my view, not much room for hesitation.

Static typing isn’t a constraint you impose on yourself: it’s a safety net that turns gambles into certainties, and hours of anxiety into a few minutes of mechanical work. The real question, at bottom, is this: when it’s time to deploy, would you rather rely on your compiler, or on a prayer?