Skip to content
Sahil Durgia/ full-stack
2 min readFull-Stack & AI

TypeScript for JavaScript Developers: Why It Stopped Being Optional

Not 'types are nice to have' — the specific class of bug TypeScript catches at compile time that JavaScript's type coercion lets through silently.

TypeScriptJavaScripttype safety

The JavaScript type coercion post earlier in this blog covered how JavaScript's runtime handles a type mismatch — usually by silently converting one value to match the other, sometimes producing a genuinely surprising result. TypeScript's actual pitch is directly connected to that post: catch the mismatch before runtime, at compile time, instead of coercing silently and hoping.

A concrete example of what it actually catches

function getDiscountedPrice(price: number, discountPercent: number): number {
  return price - price * (discountPercent / 100);
}

getDiscountedPrice(100, "20");
// Compile-time error: Argument of type 'string' is not assignable to
// parameter of type 'number'. In plain JS, this silently runs — string
// '20' coerces to number 20 via the arithmetic operators from the
// type-coercion post — and produces a value that happens to be correct
// here, purely by luck, not by design.

That "purely by luck" is the actual problem: the plain-JS version works today, by coincidence, and has no guarantee it keeps working when the caller changes. TypeScript turns a class of bug that would otherwise only surface at runtime — potentially in production, potentially rarely, depending on which code path happens to pass a wrong type — into a compile-time error, caught before the code ever ships.

Why "it's just types, my tests would catch it" undersells it

Tests catch what you thought to test. TypeScript catches an entire category of error — every call site of every typed function, across the whole codebase, checked automatically, including call sites nobody remembered existed. It also makes a codebase's own intent explicit and machine-checked: a function's signature documents what it actually expects and returns, and that documentation can't silently drift out of date the way a comment can, because the compiler enforces it on every build.

The honest cost

Real upfront cost: type definitions to write and maintain, and occasionally fighting the type system on a genuinely dynamic pattern that's awkward to type precisely. For a solo developer or a small team shipping fast, that cost is real — and it's a cost that scales sublinearly, because most of it is paid once, at the boundaries (function signatures, API response shapes), not repeatedly at every call site.

Keep reading
Next: REST vs GraphQL

Part 4 of the full-stack & AI series.