Back to blog

LanguagesOctober 21, 20254 min read

TypeScript Won. Here's What It Cost Us

USAMA DARWASHI

Laptop showing colorful TypeScript code beside a small cactus

I can tell you the moment I knew TypeScript had won. It wasn't a survey or a conference keynote. It was a junior developer looking at an old utility file and asking me, genuinely confused, why it "had no types". Not why someone had chosen JavaScript for it. Why it was broken. Somewhere along the way, plain JavaScript stopped being a choice and became a defect.

Nobody voted, but everyone complied

Angular settled this question for half of my career without asking me. Every Angular codebase I've led, from Sabr, the analytics platform we built for Oman's telecom regulator, to the Indicators system operators use for their regulatory reporting, was TypeScript from the first commit, because Angular offers no other door. The interesting battlefield was React, and that's where I watched the war end. In 2020, when I started freelancing, a React project still came with a small ritual: someone proposes TypeScript, someone else says it slows the team down, a compromise is reached, half the files get converted. By the time I was leading development at Sagan World a few years later, the ritual was gone. New React project meant tsx files, strict: true, next question. Nobody voted. The default just flipped underneath us.

What we actually gained

I want to be precise here, because the wins are real and specific, not vibes.

The biggest one is refactoring confidence. On the Ooredoo M2M platform we tracked analytics for roughly 300,000 SIM-equipped devices, and the shape of that data changed constantly as the business asked new questions. Renaming a field on a core response type and letting the compiler walk me through forty broken call sites is a fundamentally different experience from grepping and praying. I've done both. I'm not going back.

The second is contracts between teams. When the backend is Spring Boot or FastAPI and the frontend is a separate team in a separate repo, a shared set of types is the cheapest API documentation that has ever existed, because it's the only documentation that fails the build when it lies.

The third is the editor. Autocomplete that actually knows your domain, go-to-definition that lands where it should, rename that works. We talk about types as safety, but day to day they're mostly speed.

The tax we quietly pay

Here's what we don't put in the conference talks.

  • Type gymnastics. I have reviewed pull requests where a conditional mapped generic type took me longer to understand than the function it described. Types were supposed to describe the code. Somewhere we started writing code to satisfy the types.
  • Build complexity. Every TypeScript project carries a second program: the one that turns your code into code. tsconfig flags, ts-node versus tsx versus esbuild, module resolution modes with names like incantations. When the build breaks, you debug a toolchain, not your product.
  • The any economy. Strictness on paper, escape hatches in practice. as unknown as User is a lie with extra steps, and it shows up exactly where the code is most dangerous: at the boundaries, where the data actually comes from outside.
  • A generation that fears JavaScript. This one worries me most. I've interviewed developers who could write elegant generics but froze when asked what happens at runtime, because in their heads the type was the guarantee. It isn't. Types are erased before your code ever runs. The API can still send you null at two in the morning, and it will.
A type is a promise you make to yourself. The network never signed it.

Living with the winner

Would I choose TypeScript again? Yes, on every project, without hesitation. Winning was deserved. But defaults deserve scrutiny precisely because nobody questions them anymore, so this is how I try to keep the tax low on my teams.

Keep application types boring. If a type needs a blog post to explain, it belongs in a library, not in your feature code. Validate at the boundaries with something that runs, because the compiler's trust ends where the network begins. And once in a while, write a small script in plain JavaScript, just to remember that the language underneath is still there, still fine, and still what actually executes.

TypeScript won. That's the good news. The cost is only a problem if we keep pretending there isn't one.