Why Put Types on JavaScript

JavaScript runs everywhere and forgives everything — including your mistakes, right up until they reach production. TypeScript adds a static type layer on top of JavaScript that catches those mistakes at compile time, while compiling away to plain JavaScript that runs unchanged. Understanding what TypeScript actually is — a structural type checker that erases at build time — is the foundation for everything else.

If you’ve written Go, Python, or Rust and then touched a large JavaScript codebase, the absence of types feels like driving without headlights. TypeScript is the industry’s answer: a superset of JavaScript that adds static types. This series builds it from the ground up for engineers who already program and want to understand TypeScript’s type system deeply rather than cargo-culting annotations. This first post is about what TypeScript is and why it’s worth the ceremony.

The problem TypeScript solves

JavaScript is dynamically typed: a variable can hold anything, and type errors surface only when a line actually runs. user.nmae (typo) isn’t an error — it’s undefined, which flows silently downstream until something explodes far from the cause. "3" + 4 is "34"; [] + {} is a string. The language does its best to keep running, which means bugs hide instead of failing fast.

For a small script this is fine. For a large, long-lived codebase with many contributors, it’s a slow-motion disaster: refactoring is terrifying (did I break a caller three files away?), APIs are undocumented (what shape is this object?), and whole categories of bugs — typos, wrong argument order, null access, shape mismatches — reach production. TypeScript exists to move that entire class of failure from runtime to compile time, where it’s cheap to fix.

What TypeScript is

TypeScript is JavaScript plus a static type layer. Three properties define it:

That last point has deep consequences we’ll return to: because types are erased, they can’t be inspected at runtime, and any runtime validation (of API responses, user input) still needs actual runtime code. Types check your code; they don’t guard your data at the boundary.

Compilation and erasure in practice

Concretely, you write:

function greet(name: string): string {
  return `Hello, ${name}`;
}
greet("Ada");
greet(42); // ✗ compile error: Argument of type 'number' is not assignable to 'string'

tsc flags the greet(42) line at build time. Then it emits this JavaScript, with the types gone:

function greet(name) {
  return `Hello, ${name}`;
}
greet("Ada");

The annotations : string vanished. What ran the check (the compiler) is separate from what runs in production (the emitted JS). You get the safety during development and pay nothing at runtime. This separation — check-then-erase — is the essence of how TypeScript works.

Structural typing: a first taste

TypeScript’s type system is structural, not nominal — a distinction that surprises people from Java or C#. What matters is the shape of a value, not the name of its type. If an object has the right properties, it fits, regardless of what class or interface it was declared as:

interface Point { x: number; y: number; }
function dist(p: Point): number { return Math.hypot(p.x, p.y); }

const location = { x: 3, y: 4, label: "home" };
dist(location); // ✓ fine — location has x and y; the extra label is ignored

location was never declared as a Point, but it has the shape of one, so it’s accepted. This “duck typing, checked at compile time” is a defining feature of TypeScript and the subject of the next post. It’s what makes TypeScript feel flexible and JavaScript-native rather than rigid.

What you get, and the honest caveats

The payoff of the type layer is large and compounding:

The honest caveats keep you from over-trusting it: types are erased, so they don’t validate data crossing your program’s boundary (API responses, JSON, user input) — that still needs runtime checks. The escape hatch any turns checking off for a value and, used carelessly, quietly defeats the whole system (post 8). And types add friction — annotations, config, a build step — that pays off on large, long-lived codebases far more than on a throwaway script.

The mental model to carry through the series: TypeScript is a compile-time structural type checker that erases to plain JavaScript. Every feature ahead — inference, unions, generics, utility types, module config — is a way of describing your program’s shapes precisely enough that the compiler can catch mistakes before your users do.

Key takeaways

Further reading

Sources & References

What TypeScript is and how checking works