If you’re starting a new JavaScript project in 2022 without TypeScript, you’re not being “flexible.” You’re deliberately buying a future problem with interest. TypeScript has moved from preference to infrastructure: it’s how modern teams prevent avoidable bugs, document intent, and scale codebases without turning every refactor into a gamble.

JavaScript Without Types Is a Design Decision—Not a Default

Vanilla JavaScript isn’t “wrong.” It’s just ambiguous by default. When everything is dynamically typed, the language cannot help you catch mistakes that are obvious to humans: passing the wrong shape of data, calling a function with the wrong arguments, forgetting a field that another part of the app expects, or misunderstanding what a value can be.

That ambiguity might feel productive at the start. The first week is fast because you’re not fighting the compiler. But the cost shows up later when the project grows beyond a small script. The code becomes self-contradictory: naming suggests one contract, runtime reality enforces another. You end up relying on tests, runtime checks, and tribal knowledge to enforce what the type system could have enforced immediately.

My position is simple: if you’re building anything that will change—features, APIs, UI flows, integrations—then skipping types is not neutral. It’s a design decision that knowingly increases risk.

“Framework Support” Isn’t the Point—Tooling Feedback Is

Yes, most major frameworks now provide strong TypeScript support. That matters, but it’s not the whole story. The real advantage is feedback speed.

TypeScript turns the editor into a co-pilot:

  • Autocomplete becomes reliable because the IDE understands your data shapes.
  • Refactors become safer because renames and signature changes propagate through the codebase.
  • You can model domain concepts directly: UserId, OrderStatus, Email, Money—not just strings that “probably” mean the right thing.

Consider a typical bug pattern in JavaScript:

// JavaScript
function formatPrice(price) {
  return `$${price.toFixed(2)}`;
}

fetch("/api/order")
  .then(res => res.json())
  .then(order => {
    // order.total might be a string, or null, or missing
    document.body.textContent = formatPrice(order.total);
  });

This can fail at runtime in multiple ways—too late, often in production, and usually with vague error messages. With TypeScript you can encode the contract:

type Order = {
  total: number;
};

function formatPrice(price: number) {
  return `$${price.toFixed(2)}`;
}

// If order.total can be null or a string, TypeScript forces you to handle it.

Now the compiler becomes your first line of defense. That’s not “extra.” It’s a measurable reduction in avoidable bugs because you catch mismatches before you ship.

TypeScript Changes How You Think About APIs

A major reason TypeScript is worth it: it forces you to define interfaces. In other words, it makes your contracts explicit.

In JavaScript, API shapes often emerge indirectly: you infer them from usage. That works until it doesn’t. When multiple parts of the system (frontend, backend, worker processes) evolve independently, the “shape” of a thing becomes a rumor.

TypeScript helps you replace rumors with definitions. For example, if you have an API response for users:

type User = {
  id: string;
  email: string;
  displayName?: string; // optional means not always present
};

That single type becomes a shared source of truth. When someone adds displayName, removes it, or changes semantics, you’ll see the impact immediately. Even better: your IDE can guide developers to the right usage patterns because the type tells you what’s safe.

And yes, you still need runtime validation at boundaries (APIs, user input). TypeScript doesn’t eliminate runtime concerns; it prevents most internal confusion before it gets to the boundary. You stop treating “undefined behavior” as normal.

The Real Cost: Refactoring Without Confidence

Every team eventually hits the same wall: you want to refactor, but you can’t trust the codebase. The lack of types doesn’t just allow bugs—it blocks velocity.

Here’s what that looks like in practice:

  • Renaming a property becomes risky because there’s no reliable way to find all usages.
  • Changing a function signature leads to silent breakage when call sites aren’t updated correctly.
  • “We’ll fix it when it breaks” becomes the workflow.

TypeScript doesn’t magically make refactors perfect, but it makes them searchable. The compiler acts like a map of your code’s expectations. If your refactor changes a contract, TypeScript tells you where that contract is assumed.

In a larger system, that means fewer late surprises and less time spent hunting issues that should have been caught at development time. The payoff is compounding: the more you type, the easier it becomes to safely evolve the code.

Counterargument: “We’re Too Busy to Add TypeScript”

This is the most common pushback, and it sounds reasonable until you compare it to the cost of not doing it.

Adding TypeScript is usually incremental. You don’t need to “rewrite everything” before you benefit. In many projects, you can start by:

  1. Converting the most critical modules first (data fetching, domain logic, API clients).
  2. Enabling strictness gradually (or starting with a moderate level, then tightening over time).
  3. Using JSDoc types temporarily in legacy files to establish contracts while you migrate.

You also don’t have to aim for perfection overnight. The goal is to start building type-aware confidence in the areas that change most frequently.

More importantly: if you’re “too busy” to add TypeScript to a new project, you’re also too busy to maintain a codebase where bugs are discovered later and refactors take longer. TypeScript is not a luxury feature—it’s an accelerant that reduces the time you spend cleaning up avoidable problems.

Practical Rule: Type the Edges, Then Type the Core

The easiest way to get value without boiling the ocean is to apply a simple strategy:

Type the edges

Anything entering or leaving your system is a boundary:

  • HTTP requests and responses
  • WebSocket messages
  • Worker events
  • Form inputs and user-generated content

Define the types for those payloads. Then enforce them at runtime if needed. This prevents the “undefined becomes a string becomes a crash” class of bugs.

Type the core

After the edges, focus on the code that represents your domain:

  • business rules
  • calculations
  • state transitions
  • permission checks
  • mapping from API data to UI models

When your core is typed, the rest of the system becomes easier to understand and harder to misuse. That’s where TypeScript pays off most.

A small example: if you have a reducer managing UI state, types make illegal states unrepresentable. In JavaScript, “impossible” states still happen constantly because nothing stops them. In TypeScript, you can force the code to reflect reality—because the compiler won’t let you lie about it.

Conclusion: New Projects Shouldn’t Start With Hidden Risk

TypeScript isn’t optional anymore because modern development isn’t just about running code—it’s about building systems that evolve. If you start a new JavaScript project without TypeScript, you’re accepting a workflow where correctness depends on memory, tests, and luck.

TypeScript gives you earlier feedback, clearer contracts, safer refactors, and a codebase that scales without becoming haunted. If you’re building in 2022—and you want to move fast without breaking things—TypeScript isn’t an add-on.

It’s the baseline.