Nobody seriously argues about adopting TypeScript anymore. New frontend projects default to it; the holdouts are legacy codebases and the occasional throwaway script. The debate is over, TypeScript won.
But "won" is the boring part. The interesting part is what types turned out to be good for — which is more, and different, than the original pitch of "catch typos before runtime."
Types are the cheapest documentation you'll never have to update
A function signature is documentation that can't go stale, because the compiler fails the build the moment it lies.
function scheduleReminder(
userId: string,
at: Date,
channel: "email" | "push" | "sms",
): Promise<ReminderId>;
You already know almost everything about calling this without reading a single comment: what it needs, what it returns, that channel is one of exactly three strings. A comment claiming the same things could rot the next time someone adds a "slack" channel and forgets to update it. The type can't — adding "slack" to the union forces every call site to be reconsidered.
Types make refactoring a mechanical activity instead of an act of courage
In a large untyped codebase, renaming a widely-used field is genuinely scary — you're grepping strings and praying. In a typed one, you change the type and the compiler hands you the complete to-do list of everything that broke. Refactoring stops being "risky" and becomes "tedious but safe," which is exactly the trade you want. Whole categories of "we can't touch that, it's too entangled" simply dissolve.
The payoff nobody planned: types are the contract AI codegen needs
Here's the part that wasn't in the original sales pitch. The more machine-readable your boundaries are, the more reliably an AI assistant can operate inside them.
Ask a model to "add a field to this object" in untyped JavaScript and it's guessing at shape from usage. Ask it in TypeScript and the type is the spec — it knows precisely what's allowed, and its mistakes surface as compile errors instead of 2am production incidents. Types turn "generate plausible code" into "generate code that provably fits."
This flips an old objection on its head. People used to say types slowed them down. In an AI-assisted workflow, types speed you up, because they're the guardrail that lets you accept generated code with confidence instead of auditing every line by hand.
A few habits that compound
If types are this leverage-rich, it's worth writing them with intent rather than appeasing the compiler:
-
Prefer unions over booleans + optionals.
status: "loading" | "error" | "ready"beats three independent boolean flags that can contradict each other. -
Name your domain types.
type Cents = numberdocuments intent at every use site and lets you tighten it later. -
Avoid
any; reach forunknownand narrow.anyis a hole in exactly the safety net you're paying for. - Let inference work. You don't need to annotate everything — annotate the boundaries (function signatures, exported APIs) and let the rest flow.
The takeaway
TypeScript's real win wasn't catching typos. It was turning your codebase into something with explicit, enforced contracts — and contracts turn out to be exactly what fearless refactoring, reliable tooling, and trustworthy AI assistance all quietly depend on.
We adopted types to prevent a class of bugs. We're keeping them because they're the substrate everything else now builds on. Worth investing in writing them well.
Top comments (6)
TypeScript slows development and restricts creativity. If you're a good JS dev, TS is totally unnecessary. If you really must have some types, use JSDoc... you can pepper types in where you need them, remove the compile step, and retain the full flexibility and speed of just using JS.
The habit I'd rank highest in that list is naming the domain types.
type Cents = numberdocuments intent, but it's still just a number to the compiler, so nothing stops a raw amount or a Milliseconds value sliding into a Cents slot. Brand it and the compiler actually rejects the mismatch. That matters more with generated code, because the model will happily pass a number that's the right primitive and the wrong quantity, and an alias won't catch it where a branded type will.The underrated win is that types changed team communication, not just bug counts. A good type tells the next developer what shape the system will accept, and the compiler keeps that statement honest. That matters even more with AI coding tools because types become executable context for the agent.
"Types are documentation that can't go stale" is the insight that actually shifts the value proposition here. We've been selling TypeScript as a runtime error catcher, but the deeper win is that type signatures are the only form of intent that the compiler, your IDE, your teammates, and increasingly your AI coding assistant can all consume simultaneously. When you're working in a codebase where an LLM is generating functions, an explicit type contract is what keeps hallucinated return shapes from propagating silently. The more autonomous the tooling, the more valuable types become as a shared language of intent rather than just a safety net.
Do you see branded types like
ReminderIdbecoming more common as teams lean into AI assisted development?I'm curious if you've seen a
Last week, one of my devs said, "Aha! TypeScript would have stopped that bug!"; they were right. A team of 30 uncovered a fault that TS would have stopped. In 30 months of shipping and 48 months of life, something slipped by. We found it and fixed it; the fault wasn't serious, but it was there. When I consider whether we should have used TS, my view is still, "no way". All of that boilerplate, complexity and convincing the compiler we're not idiots versus one minor bug from before we JSDoc'd our core libraries. If we'd built in TS we'd be 6 months behind where we are.
I have a very modern, very powerful application used by 1000+ blue-chip companies. I ship releases daily and run 1000s of tests, which is what makes it robust. I wouldn't choose TS because the architecture I use would be noisy. No AI agent has any problem building in it - and it's fewer tokens per generation too, ofc.