Overview

Use TypeScript for any codebase that crosses 5k lines, has more than one author, or expects to live past the quarter. Use plain JavaScript only for one-off prototypes, glue scripts under 1k lines, or interactive notebooks. The “JS for prototypes, TS for products” rule covers 95 percent of decisions in 2026. The cost of TypeScript is the build step and a learning curve; the cost of JavaScript past a certain size is refactor paralysis and runtime bugs the type checker would have caught. See typescript for the TypeScript rule set.

When TypeScript wins

TypeScript is the right pick for anything ambitious or anything that will see a refactor.

  • Refactor confidence: rename a field, the compiler lists every call site. Plain JS requires text search and prayer.
  • API contracts: shared types between frontend and backend (tRPC, Zod, Prisma) catch the wrong-field, wrong-shape bugs at compile time.
  • Library coverage: the npm ecosystem ships types for almost everything; DefinitelyTyped covers the rest.
  • IDE support: autocomplete, jump-to-definition, and inline docs work on TypeScript; on JS they work poorly past one module deep.
  • Onboarding: new contributors read types as documentation. JS docstrings drift; types do not.
  • LLM agents code better against TS than JS because the type checker grounds output. See structured-output.

When JavaScript wins

Plain JavaScript is the right pick when the build step is overhead the project will never recoup.

  • One-off scripts under 1k lines: a migrate.js that runs once and dies does not need types.
  • Interactive notebooks (Observable, Jupyter with JS kernel) where types fight the REPL flow.
  • Prototypes meant to be thrown away inside 48 hours. Anything else is a prototype that lived; convert it.
  • Browser-only snippets running directly in a <script> tag with no build pipeline (rare in 2026 but real for some embeds).
  • Codebases where the team has zero TS experience and the project is small enough that the migration cost outweighs the bug cost. Re-evaluate this at every quarter.

Trade-offs at a glance

DimensionTypeScriptJavaScript
Refactor safetyHigh; compiler catches breakageLow; relies on tests
Build complexitytsc, esbuild, or swc requiredNone; runs directly
Library typesBuilt-in or DefinitelyTypedOften missing; JSDoc helps
Learning curveModerate; generics, narrowing, satisfiesNone over plain JS
Runtime costZero; types erasedZero
LLM agent outputType checker grounds outputModels hallucinate property names
IDE supportExcellentDecent with JSDoc only
CI timeAdds a typecheck stepNone
Team scalingStrong above three developersFalls apart past five

Migration cost

JS-to-TS is the only direction that matters; the reverse never makes sense.

  • Add tsconfig.json with allowJs: true and checkJs: false. Rename files one at a time from .js to .ts. Use // @ts-nocheck to defer hard files; remove the pragma when the file is ready.
  • A 50k-LoC JS app typically migrates in two to four engineer-weeks of focused work; longer if hooks, classes, or dynamic property access dominate.
  • Start with strict: false, then turn flags on one by one: noImplicitAny, then strictNullChecks. See typescript-strict-mode.
  • Do not migrate by inserting any everywhere. That ships the build step without the safety. Use unknown and narrow.

Recommendation

  • New project, any size, any team count: TypeScript with strict: true. See typescript-tsconfig.
  • Existing JS codebase over 5k lines, two or more contributors: migrate to TypeScript starting from the leaf modules.
  • One-off data migration script, throwaway shell glue: plain JS or a .mjs file with JSDoc types if you want autocompletion.
  • Library author: ship TypeScript, publish both ESM and CJS, ship .d.ts files.
  • Frontend with React: TypeScript is non-optional past prototype stage. See react.