Skip to content
← All posts
5 min read Dawid Skłodowski

TypeScript for Rubyists

Ruby developers love dynamic typing — so why is TypeScript worth your attention? A deep-dive on gradual static typing for JavaScript, what it catches, where it helps most, and how a Rubyist should think about types on the front end.

Ruby developers tend to like dynamic typing — duck typing, flexibility, no ceremony — so TypeScript can look like exactly the bureaucracy we left other languages to avoid. But TypeScript has gone from a curiosity to a near-default for serious JavaScript in 2019, and the reasons are worth understanding even if (especially if) you are a committed Rubyist. The front end is a different environment from a Rails backend, and the calculus on types comes out differently there. Here is how to think about it.

What TypeScript actually is

TypeScript is JavaScript with an optional static type layer, compiled (by tsc) down to plain JavaScript that runs anywhere JS does. The crucial word is optional and gradual: any valid JavaScript is valid TypeScript, so you adopt types incrementally, adding them where they help and leaving the rest as-is. You annotate, the compiler checks consistency before the code ever runs, and then the types vanish — they exist only at compile time:

function total(items: LineItem[]): number {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}

interface LineItem {
  sku: string
  price: number
  quantity: number
}

The compiler now knows total takes an array of LineItem and returns a number, and it will reject, before runtime, code that calls it wrong or accesses a field that does not exist. That “before runtime” is the whole value proposition.

What it catches, and why it matters more on the front end

The errors TypeScript catches are the dumb, common ones that plague JavaScript specifically: undefined is not a function, a typo’d property name returning undefined and propagating, the wrong argument order, a shape that changed and broke three call sites. In Ruby these mistakes also happen, but you catch them quickly — with a fast, comprehensive test suite and a tight edit-run loop. On the front end, the calculus is different in ways that favour types:

  • The “test suite” is a human clicking. Front-end code paths are often verified by someone exercising the UI, not by exhaustive automated tests. A type error that Ruby’s specs would catch can ship to the browser unnoticed.
  • The feedback loop is slower and the surface is huge. A typo in a rarely-hit UI branch might not surface until a user finds it. The compiler finds it instantly, in your editor.
  • Refactoring JavaScript is scary; refactoring TypeScript is safe. Rename a field or change a function’s shape, and the compiler lists every place that now breaks. This is the single biggest day-to-day win — you can change code with confidence the tools could not give you in plain JS.

So the Rubyist’s instinct — “I don’t need types, I have tests and duck typing” — is reasonable on the backend and weaker on the front end, where the safety net of fast comprehensive tests is usually thinner and the cost of a runtime error is a broken page a user sees.

The features a Rubyist will appreciate

Several TypeScript ideas map onto things you already value:

  • Type inference means less ceremony than you fear. You do not annotate everything; TypeScript infers most types. const total = items.reduce(...) is known to be a number without you saying so. You annotate the boundaries — function signatures, data shapes — and let inference handle the interior, which keeps it from feeling like Java.
  • Interfaces describe data shapes — the JSON your API returns, the props a component takes — which doubles as documentation that cannot go stale, because the compiler enforces it.
  • Editor support is transformative. Because the editor knows the types, you get accurate autocomplete, inline error highlighting, and “jump to definition” that actually works. For many developers this tooling alone is worth it — it is like having a knowledgeable pair watching as you type.
  • Union types and strictNullChecks make the absence of a value explicit. string | null forces you to handle the null case, turning the front end’s most common crash — cannot read property of undefined — into a compile error.

How to adopt it, gradually

The right way to bring TypeScript into a project is incrementally, which suits its gradual nature:

  • Start with tsconfig.json permissive, then tighten. Turn on strict mode eventually (it is where most of the value is, especially strictNullChecks), but you can begin loose and ratchet it up as you convert code.
  • Type the boundaries first — your API responses, your core data models, the props of your components — because that is where wrong shapes cause the most damage and where types pay off fastest.
  • Use any as an escape hatch, sparingly. any opts a value out of checking; it is useful for migrating gradually but every any is a hole in the safety net, so treat reducing them as the ongoing work.
  • Lean on @types packages. The community publishes type definitions for most npm libraries (@types/react, etc.), so you get typed third-party code for free.

For a Rails app with a Webpacker/React front end, this fits naturally: type the components and the API client, leave the simpler scripts alone, and tighten over time.

The honest trade-offs

TypeScript is not free. There is a compile step (already there if you use Webpack), a learning curve for the type system (which gets genuinely advanced at the edges), and some ceremony for very simple scripts where it earns nothing. The judgment, much like every other “should I add this structure” question in these posts: TypeScript’s value rises with codebase size, team size, and how long the code must live. A throwaway script or a tiny widget does not need it; a large, long-lived front-end application maintained by a team is exactly where it pays for itself many times over.

Verdict

A Rubyist’s scepticism about static typing is well-earned on the backend, where fast tests and duck typing serve you well — but the front end is a different environment, and TypeScript’s case is strong there. It catches the dumb, common JavaScript errors before runtime, makes refactoring safe instead of scary, and turns your editor into a knowledgeable assistant — and its gradual, inference-heavy design means far less ceremony than the word “types” suggests. Adopt it incrementally, type your boundaries first, turn on strict as you go, and reserve any for the migration. For a large, long-lived front end — which is most serious front-end work in 2019 — it is no longer the bureaucracy a Rubyist fears, but the safety net the front end was missing.