The part worth reading is where that line sits in 2026, because it moved. Bun and Deno have run TypeScript directly for years. In late 2024 and into 2025, Node finally caught up and joined them. That quietly deleted the single biggest reason people reached for plain JavaScript: getting started fast.

I ship mobile and web apps in TypeScript and reach for plain JavaScript on quick scripts. The decision shows up every time I create a file. Here is how I make the call now, and why the two languages are closer than they have ever been.

Pick by lifespan: throwaway code maps to plain JavaScript, revisited code to JavaScript plus JSDoc, maintained code to TypeScript

What changed in 2025: your runtime reads TypeScript

The build step is mostly gone. Node 23.6 made native type stripping work with no flag, so node app.ts runs without tsc, ts-node, or a bundler. Recent Node 22 LTS builds do it too.

Here is the whole setup:

# Node 22.6 added --experimental-strip-types.
      # Node 23.6 made it the default. This just works:
      node app.ts
      

Type stripping does exactly what the name says. It removes the type annotations and leaves the JavaScript untouched, so your .ts file becomes valid JS at parse time with no separate compile.

// app.ts, runs directly on modern Node, Deno, and Bun
      function greet(name: string): string {
        return `hello ${name}`;
      }
      console.log(greet("world"));
      

Two honest catches. First, stripping runs your code but does not check it. The types are deleted, not verified, so you still run tsc --noEmit or lean on your editor to catch type errors. Second, stripping cannot handle syntax that emits real code, like enum and namespace. Those you compile with tsc or avoid. Skip them and your TypeScript runs anywhere JavaScript runs. The TypeScript team turned that second catch into a feature.

TypeScript earns its keep when code outlives the week

TypeScript wins the moment more than one person, or more than one week, touches the code. The payoff is not "fewer bugs" in the abstract. It is the specific bug that ships because a value had the wrong shape and nobody noticed.

function sendInvoice(userId: string, amountCents: number) { /* ... */ }

      // Plain JS: this runs, then misbehaves in production.
      // You swapped the arguments and nothing warned you.
      sendInvoice(400, "user_42");

      // TypeScript: red squiggle before you ever save.
      // Argument of type 'number' is not assignable to 'string'.
      

The bigger win is refactoring. Rename a field on a shared type and the compiler hands you every one of the forty places that now break. On our own apps the TypeScript line pays for itself the first time a refactor touches that many files. Autocomplete that actually knows your data is the daily version of the same payoff.

When the type tax is not worth paying

Plain JavaScript still wins for code with a short life and a single author. A thirty line script does not earn a tsconfig.json, a pile of @types packages, and a fight with a generic. The type tax is real, and on throwaway work it never pays back.

The same goes for learning, for a quick prototype you will rewrite anyway, and for the small config file nobody touches again. Reaching for TypeScript there is ceremony, not safety.

If you want a middle ground, JSDoc gives you type checking with zero new syntax:

// @ts-check
      /** @param {string} name */
      function greet(name) {
        return `hello ${name}`;
      }

      greet(42); // editor flags this, and the file is still plain .js
      

Add // @ts-check to the top of a .js file and your editor checks it against JSDoc comments. You get the safety net without the build story. Plenty of large projects ship exactly this way on purpose.

The two languages are converging

TypeScript and JavaScript are growing toward each other, not apart. The clearest signal is a flag added in TypeScript 5.8: erasableSyntaxOnly.

// tsconfig.json
      { "compilerOptions": { "erasableSyntaxOnly": true } }
      

Turn it on and TypeScript refuses to compile the parts that are not pure types: enum, namespace with runtime values, constructor parameter properties, and import = aliases. It bans exactly the four constructs that native type stripping cannot run. The replacement for an enum is a plain union:

// Not erasable, emits a runtime object:
      enum Role { Admin, User }

      // Erasable, disappears completely after stripping:
      type Role = "admin" | "user";
      

This is the TypeScript team steering the language toward "JavaScript with types you can delete." It lines up with the TC39 Type Annotations proposal, a Microsoft backed effort to let JavaScript engines treat type syntax as comments. That one has sat at Stage 1 since 2023 and stalled there, partly because native type stripping already solved the same problem in practice.

Tooling is moving the same way. TypeScript 6.0 shipped in March 2026 as the last release built on the original compiler. Its successor, TypeScript 7.0, is a native port written in Go that Microsoft says is about ten times faster to build. The friction that made TypeScript feel heavy is getting designed out.

So which should you use in 2026?

Default to TypeScript for anything you will maintain, and reach for plain JavaScript or JSDoc for anything you will not. The decision is about lifespan and blast radius, not ideology.

Decision flow: if a file is maintained past this week use TypeScript, otherwise plain JavaScript for one-off files or JSDoc for scripts you will revisit

Run the file through one question: will you still be running this past this week? If yes, the safety, the autocomplete, and the safe refactors pay for themselves, and your runtime executes the .ts file directly anyway. If no, and it is a single file or a quick prototype, plain JavaScript keeps you fast. The in-between case, a script you will revisit but do not want to configure, is what JSDoc is for.

The old framing was a war: dynamic freedom against static safety. That war is over. TypeScript won the code you keep, JavaScript owns the code you toss, and native type stripping erased the wall that used to stand between them. Pick by how long the code will live, and you will be right almost every time.


I make this call every time I create a file, building apps and tools at astraedus.dev. If you have a real case where the lifespan rule breaks, I want to hear it: astraedus.dev or [email protected].

Get the next one in your inbox, subscribe at astraedus.dev.