BYBOWU > Blog > Web development

Node.js TypeScript Support Is Now Native: Do This

blog hero image
As of November 11, 2025, Node 25.2 marks native TypeScript type stripping as stable. That’s a real shift: you can now run .ts files directly without ts-node, Babel, or SWC in many cases. But there’s a catch—the runtime ignores tsconfig, requires explicit import type, and refuses .ts files in node_modules. If you ship Node apps, this changes how you design packages, set up CI, and think about monorepos. Here’s what actually changed, what still doesn’t work, and a pragmatic checklist ...
📅
Published
Nov 22, 2025
🏷️
Category
Web development
⏱️
Read Time
11 min

On November 11, 2025, Node 25.2 marked native TypeScript type stripping as stable. In plain English: Node can execute many .ts files directly, no extra loader required. Node.js TypeScript support just moved from “experimental and awkward” to “built in and practical”—with sharp edges you need to respect. A quick follow‑up, 25.2.1, landed November 16 to smooth roughness while keeping the new defaults.

If you maintain apps, libraries, or a monorepo, this isn’t a minor footnote. It touches your import rules, your CI matrix, and how you ship internal packages. Let’s break down what shipped, where teams get tripped up, and exactly how to adopt it without firefighting.

What Node.js TypeScript support actually shipped

Node’s new native path is type stripping: the runtime erases TypeScript syntax that has no runtime meaning, then runs the result as JavaScript. No transpiling to older JS targets, no JSX, and no tsconfig reading. The feature was enabled by default earlier in the 23.x line; as of 25.2 it’s marked stable. Meanwhile, Node 24 remains Active LTS—useful for production teams that prefer stability with fewer changes.

Two modes coexist now, and you should pick deliberately:

  • Built‑in “type stripping” (fast, minimal): run .ts directly if you only use erasable TypeScript features. Node ignores tsconfig and expects valid, modern JS after types are removed.
  • Full TypeScript (max features): use a third‑party tool (tsx, ts-node, SWC, Babel) to honor tsconfig, downlevel transforms, JSX, path aliases, decorators, and more.

Here’s the thing: “native” doesn’t mean “everything TypeScript supports.” It means “everything TypeScript the runtime can erase without changing behavior.” That’s a powerful baseline—if you adjust a few habits.

What works—and what won’t—under type stripping

Works out of the box:

  • Regular TS annotations: types on variables, params, and returns.
  • import type and export type statements.
  • Running .ts files directly via node file.ts (in supported versions).

Won’t work (because they require code transforms or emit):

  • Enums, parameter properties in classes, namespaces with runtime code, legacy module syntax like import =/export =.
  • JSX (use a compiler), experimental syntax that needs transforms, and decorators that emit code.
  • tsconfig features in general: Node doesn’t read tsconfig, so no paths aliases, no target downleveling, no module rewriting.

Gotchas the runtime enforces:

  • You must use import type for type‑only imports; otherwise Node treats them as value imports and errors at runtime.
  • Relative imports of TS sources need explicit .ts extensions in erasable mode.
  • Node refuses to run .ts inside node_modules. That discourages publishing TS‑only packages and surprises monorepos that cross‑import raw .ts between packages.
  • TypeScript in REPL isn’t supported; stdin and eval modes are limited.

Pro tip: if you want the compiler to enforce “erasable‑only” constraints during development, enable an “erasable syntax only” check in your TS config and pair it with verbatimModuleSyntax to require explicit import semantics. That codifies the same rules Node expects at runtime.

Do I still need ts-node, tsx, or SWC?

Probably—somewhere in your stack. Native stripping is perfect for CLIs, scripts, microservices, and internal tools that stick to erasable syntax. But if you rely on JSX, decorators, older targets, or tsconfig paths, keep your compiler. Many teams will run hybrid: native stripping for quick scripts and local tooling, a build step for apps and libraries.

If you want a single happy path for both dev and prod, define two execution lanes in your docs/scripts: “node” for erasable‑only projects, and “tsx” (or similar) when features exceed what Node can erase. Future contributors will thank you.

Will this break my CI or monorepo?

The biggest breaking pattern is cross‑package TS imports. With native stripping, importing packages/foo/src/index.ts from another workspace will fail in Node because that path lives under node_modules when linked. You now have three options:

  • Keep a build step for shared packages so consumers import compiled .js.
  • Use subpath exports with “#” internal aliases in package.json and resolve to built JS.
  • Use a dev‑time loader (tsx) across the repo; emit JS only for prod or publishing.

Also expand your CI matrix. Test on Node 24 (Active LTS) and Node 25.2+ to catch subtle differences. If you use GitHub Actions, be explicit with your runners and dependency caching. We’ve covered secure workflow design in depth—see our guide on getting pull_request_target right in GitHub Actions—then apply the same rigor here. Treat runtime upgrades as supply‑chain changes, not just “bump Node and pray.”

Data points and timelines worth knowing

Dates matter for rollout planning:

  • November 11, 2025: Node 25.2 marks type stripping stable.
  • November 16, 2025: Node 25.2.1 ships a quick patch.
  • Node 24 is Active LTS through 2027 on the usual LTS cadence; it already supports type stripping without the scary warning in later 24.x.

Translation: teams can adopt the feature on LTS or Current. If you want the absolute least churn, use Node 24 and enable type‑stripping workflows selectively; if you want the latest semantics and V8 improvements, standardize on Node 25.2+. Either way, lock versions in production.

How this affects frameworks you already use

Next.js 16 made headlines this fall with Cache Components and a proxy.ts file replacing middleware.ts for Node‑runtime interception. It also dropped Node 18 support and requires Node 20.9+. If your organization is jumping from Node 18 or early 20.x to 24/25, align the two migrations. Read our hands‑on take: Cache Components and the new proxy.ts in Next.js 16.

Across the board, expect ecosystem churn as tools assume newer Node baselines. That’s the price of standardization—and the benefit is simpler, faster builds with fewer bespoke loaders.

Developer terminal running a TypeScript file in Node

Let’s get practical: a 90‑minute rollout checklist

Use this to pilot native Node TypeScript in a real repo today.

1) Pin your runtime and runners

Pick one primary runtime: Node 24.x (LTS) or 25.2+. Pin the exact version in .tool-versions, .nvmrc, Docker base images, and CI runners. Add a secondary job to your CI matrix for the other line to surface incompatibilities early.

2) Make TypeScript “erasable by design”

In projects that will rely on native stripping, configure your TS compiler to match runtime expectations. Require explicit type imports, disable import elision, and allow TS extensions for relative imports. A minimal mental model:

  • Use verbatimModuleSyntax: true to keep type/value imports accurate.
  • Use type‑only imports consistently (import type { Foo }).
  • Allow importing .ts extensions in dev; compile to .js for published packages.

During adoption, run your compiler with an “erasable only” check to catch enums, parameter properties, and other non‑erasable constructs in PRs instead of at runtime.

3) Fix your imports

Audit for these patterns and clean them up:

  • Replace type‑only value imports with import type.
  • Ensure relative imports include file extensions when you intend to execute raw .ts (e.g., import { x } from "./util.ts" in erasable projects). Don’t mix and match with compiled output.
  • Remove paths alias reliance where Node runs code directly; prefer subpath imports from exports in package.json.

4) Keep node_modules pure

Don’t publish raw .ts. For internal monorepos, either build shared packages to JS before consumption or keep dev‑time execution under a loader like tsx. As a rule: consumers import JS; developers may run TS.

5) Update CI and caches

Cache the right things for each lane. If you run native TS, there’s no transpile artifact to cache—great. If you compile, cache your build output and TypeScript incremental files. Rotate any old auth tokens and audit npm scopes while you’re in there; we’ve written about npm token changes that broke CI for teams—use that checklist to avoid being surprised mid‑deploy.

6) Observability and rollout

Gate the change behind service flags or staged rollouts. Track error rates and cold‑start times before/after. For larger estates, prioritize leaf services, then shared libraries, then high‑traffic front doors.

People also ask: common adoption questions

Is “native” faster than compiling?

Often yes for startup and small scripts, because there’s no transpile step. For big apps, the difference is nuanced. You’ll still run a build for bundling, dead‑code elimination, and minification. Think of native stripping as a simpler dev/runtime path, not a replacement for production builds.

Can I mix native stripping and a compiled app?

Yes. Many teams will run native TS for internal tooling and scripts while compiling the main app. Just be consistent inside each package so imports don’t cross erasable and compiled boundaries.

What about security?

Native stripping reduces your dependency footprint by removing loaders and plugins. That’s good. But runtime upgrades carry risk—new APIs and semantics land quickly. Treat Node bumps like any dependency upgrade: pin, test, roll out gradually, and lock CI. If you need a refresher on release hygiene, our LTS perspective in what long‑term support changes for your web stack applies here too.

Tradeoffs vs Deno and Bun

Deno and Bun have long advertised first‑class TypeScript support, including permissions, bundling, and fast startup. Node’s angle is different: keep the runtime small, embrace modern web APIs, and let the ecosystem handle compilation when you need transforms. If you love no‑config DX, Deno/Bun still feel great. If your team or platform standardizes on Node, the gap is much narrower now—and your build graph gets simpler.

Comparison illustration of TS features across Node, Deno, and Bun

Risks and edge cases we’ve seen

Expect rough edges during the first weeks of a feature going stable. Some toolchains that introspect syntax or depend on subtle module behaviors may fail tests on a new Node minor; most issues resolve quickly. Keep a pinned version for production and advance your staging runners a few days ahead so you can catch surprises without stopping merges.

Also, clarify team conventions: when to write import type, when to include file extensions, whether raw .ts can run in prod, and how to handle shared packages. Ambiguity creates churn; a two‑page “Runtime & TS Conventions” doc saves hours.

A simple decision framework for your team

Use this three‑question flow to pick your approach per app/package:

  1. Do we need transforms (JSX, decorators, down‑leveling, paths)? If yes, use a compiler (tsx/SWC/Babel) and emit JS; don’t rely on native stripping in prod.
  2. Are we a script/CLI or service that can stick to erasable TypeScript? If yes, run .ts natively and add a linter rule to prevent non‑erasable constructs.
  3. Are we publishing a package? Always publish JS (plus types). Keep TS in source, not in the published bundle.

Write the decision down in your CONTRIBUTING.md. New engineers shouldn’t have to guess.

Whiteboard checklist for rolling out native TypeScript in Node

What to do next

Developers:

  • Add a Node 24 + 25.2 job to CI and pin versions locally.
  • Enable explicit import type rules and forbid non‑erasable constructs where you plan to run .ts natively.
  • Split your repo into erasable (native) and compiled lanes. Don’t mix.
  • Document import/file‑extension conventions and publish‑time build rules.

Engineering managers and owners:

  • Schedule a half‑day migration workshop. Start with leaf services and internal tools.
  • Set a policy for publishing JavaScript artifacts only, with types for DX.
  • Budget time for dependency hygiene and token refreshes; upgrade days are great moments to fix CI secrets and policies. Our guide to handling GitHub account changes pairs well with runtime upgrades.
  • When you move Node for your Next.js apps, align with framework changes. If you’re leaning into modern caching and network boundaries, our breakdown of Next.js 16’s Cache Components and proxy.ts shows how to think about it end‑to‑end.

If you want help crafting a clean, low‑risk plan across multiple apps, we do this routinely for clients—runtime upgrades, CI hardening, and framework moves in one coordinated sprint. See how we work on our services page and reach out via contacts.

Zooming out

Native Node.js TypeScript support won’t eliminate compilers. But it trims the fat from everyday tasks—scripts, CLIs, internal services—and forces healthier boundaries in how we publish and import code. Less magic, more explicitness. That’s a win. Adopt it with clear conventions and a small pilot, and you’ll ship faster with fewer moving parts.

Written by Viktoria Sulzhyk · BYBOWU
3,521 views

Get in Touch

Ready to start your next project? Let's discuss how we can help bring your vision to life

Email Us

[email protected]

We'll respond within 24 hours

Call Us

+1 (602) 748-9530

Available Mon-Fri, 9AM-6PM

Live Chat

Start a conversation

Get instant answers

Visit Us

Phoenix, AZ / Spain / Ukraine

Digital Innovation Hub

Send us a message

Tell us about your project and we'll get back to you

💻
🎯
🚀
💎
🔥