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.jsthat 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
| Dimension | TypeScript | JavaScript |
|---|---|---|
| Refactor safety | High; compiler catches breakage | Low; relies on tests |
| Build complexity | tsc, esbuild, or swc required | None; runs directly |
| Library types | Built-in or DefinitelyTyped | Often missing; JSDoc helps |
| Learning curve | Moderate; generics, narrowing, satisfies | None over plain JS |
| Runtime cost | Zero; types erased | Zero |
| LLM agent output | Type checker grounds output | Models hallucinate property names |
| IDE support | Excellent | Decent with JSDoc only |
| CI time | Adds a typecheck step | None |
| Team scaling | Strong above three developers | Falls apart past five |
Migration cost
JS-to-TS is the only direction that matters; the reverse never makes sense.
- Add
tsconfig.jsonwithallowJs: trueandcheckJs: false. Rename files one at a time from.jsto.ts. Use// @ts-nocheckto 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, thenstrictNullChecks. See typescript-strict-mode. - Do not migrate by inserting
anyeverywhere. That ships the build step without the safety. Useunknownand 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
.mjsfile with JSDoc types if you want autocompletion. - Library author: ship TypeScript, publish both ESM and CJS, ship
.d.tsfiles. - Frontend with React: TypeScript is non-optional past prototype stage. See react.