top | item 44929260

Node.js is able to execute TypeScript files without additional configuration

443 points| steren | 6 months ago |nodejs.org

262 comments

order

lunarcave|6 months ago

I think this + node:test makes Node.js a pretty compelling sensible default for most things now. Running things with `tsx` was such a QoL improvement when it happened, but it didn't solve everything.

Runtime type assertion at the edges is mostly solved through `zod` and tools like `ts-rest` and `trpc` makes it so much easier to do full-stack Typescript these days.

madeofpalk|6 months ago

This. It's 2025 and the node ecosystem is finally usable by default!

ESM modules just work with both Node and Typescript, Node can run .ts files, and there's the a good enough test runner built in. --watch. The better built in packages - `node:fs/promises` - are nice with top-level await for easier async loops.

It took a while to convince everyone involved to just be pragmatic, but it's nice now.

_heimdall|6 months ago

I'm very much in favor of TS support directly in node. vitest has made it easier these days, but I've lost too much time over the years getting the balance just right when configuring test environments for .ts files.

trpc and ts-rest are a different animal in my opinion. I'm happy to use either one but won't deal with them in production. For trpc that's mainly due to the lack of owning API URLs and being able to more clearly manage deprecating old URLs gracefully.

For ts-rest I just tend to prefer owning that setup myself, usually with zod and shared typings for API request/response pairs. It also does irk me every time I import what is clearly an RPC tool named "-rest"

edem|6 months ago

i switched to python a while ago. it has batteries included. i feel so much better now that i dont have to debug all the quirks of a half-baked system.

rs186|6 months ago

Let's see if Sveltr converts their codebase back to TypeScript

socalgal2|6 months ago

Does this run tsx? Even with the types stripped you still need the JSX transformed to JavaScript

reactordev|6 months ago

This is great up until you get to the fact that typescript will not be accepted under node_modules [0].

That leads me to ask, what about project dependencies? I wrote a lib for my data models in typescript and I want to import that into my app in node, in typescript? Does the rule only apply to npm packages? There’s opportunity here…

I wrote a runtime in golang that runs typescript (well, JavaScript in general). The grafana folks have sobek that all they need is to add type striping. I feel like if there’s one runtime where typescript could be adopted fully and it would change the world is Node.js. No transpiler, no typescript-go, no rust (well, maybe some rust ;) just a great parser that will keep track of the source map and types in debug mode (for tracing).

Either way, kudos to the node team, contributors, for pulling in the goal posts to make the kick to launch shorter. I’m still a fan of bun, and my own runtime, but node is the standard by which we all are kinda following. I also like that the embedding api is simple and clean to use now so if you want to make an executable, you can.

[0] https://nodejs.org/api/typescript.html#type-stripping-in-dep...

rovingeye|6 months ago

I made the same comment here https://news.ycombinator.com/item?id=44931575

"To discourage package authors from publishing packages written in TypeScript"

I tried to use it with private packages but that doesn't work either, apparently node doesn't even read the "private" field.

spankalee|6 months ago

Compile the packages to JavaScript before publishing. We absolutely should but be publishing TypeScript to npm.

sisve|6 months ago

Impressed with what Node is doing the last years, deno and bun has really made Node focus and improve. It was stuck for a while

mattlondon|6 months ago

What are the recent improvements in node itself?

Last actually note-worthy improvement I heard of was properly supporting import/export (although do you still need to use the .mjs hack?), but I've been out of the loop here for sometime so would be nice to know what they've added since.

pier25|6 months ago

They added type striping, not full TS support.

And the biggest issue with Node IMO is that the standard lib still forces you to rely on endless npm dependencies.

Node is still very much stuck.

pjmlp|6 months ago

Have them though?

On the projects I am involved, they could even not exist, only node LTS releases matter, and the most recent projects are still node 20.

rmonvfer|6 months ago

I’m not a heavy JS/TS dev so here’s an honest question: why not use Bun and forget about node? Sure I understand that not every project is evergreen but isn’t Bun a much runtime in general? It supports TS execution from day 1, has much faster dependency resolution, better ergonomics… and I could keep going.

I know I’m just a single data point but I’ve had a lot of success migrating old node projects to bun (in fact I haven’t used node itself since Bun was made public)

Again, I might be saying something terribly stupid because JS/TS isn’t really my turf so please let me know if I’m missing something.

port11|6 months ago

I've mainly worked with Node for now 8 years, and recently switched to Deno. Even that switch was hard to do; not because things don't work, but you don't know when they won't.

Node has its share of flaws, but it's the de facto baseline against which things are tested and developed. I'm somewhat more comfortable working with The Main Thing.

The JavaScript ecosystem is nightmarish enough that many developers don't want to switch to the Next Cool Thing. I think many of us have had enough fatigue caused by new build tools, new bundlers, new runtimes, etc.

As of right now, Bun is not compelling enough for the potential headaches down the line.

(Maybe there won't be any, but I've spent weeks dealing with incompatibilities caused by a single TS minor update (which should've been breaking). Days chasing after dependency problems, after missing docs, etc.)

rglover|6 months ago

> why not use Bun and forget about node?

Because Node is controlled by and maintained via the OpenJS Foundation. Bun is a venture-backed startup. That would be fine, but multiple, direct requests from me to the founder (Jared) about what the business model is (what I personally need to say "yes" or "no") have gone without response every time.

That led me to the assumption that Bun may very well be technically superior (or at least on the track to being so), but I can't bet anything significant or long-term on it. I need to know that this isn't just an exit vehicle for the founder (masquerading as a desire to fix JS server runtimes). Silence to a simple (dare I say, obvious) question doesn't bode well.

notsylver|6 months ago

I have tried fully switching to bun repeatedly since it came out and every time I got 90% of the way there only to hit a problem that couldn't be worked around. Last I tried I was still stuck on some libraries requiring napi functions that weren't implemented in bun yet, as well an issue I forget but it was vaguely something like `opendir` silently ignoring the `recursive` option causing a huge headache.

I'm waiting patiently for bun to catch up because I would love to switch but I don't think its ready for production use in larger projects yet. Even when things work, a lot of the bun-specific functionality sounds nice at first but feels like an afterthought in practice, and the documentation is far from the quality of node.js

prmph|6 months ago

I agree. I've tried the Node TS and test runner features, and they are still (not yet) as good as Bun's. So for now sticking with Bun for those.

Really, in the Node ecosystem you eventually learn not to put all your eggs in one basket. Different things excel in different aspects. Here is my preferred setup for now:

Bun.js: As a Node runtime, and for TS execution and test running. I tried lots: TSX, TS-Node, Node itself

NPM For executing tooling scripts

PNPM For installing dependencies. It's simply better than the rest (npm, yarn, bun) for several reasons

Biome.js For linting (superior to every other tool I tried)

motorest|6 months ago

> I’m not a heavy JS/TS dev so here’s an honest question: why not use Bun and forget about node?

Why would you switch from runtime A to runtime B? I mean, you presented no reason at all, let alone a compelling one, to pick either one. So what leads you to believe it is a reasonable idea to waste time switching runtimes?

madeofpalk|6 months ago

> why not use Bun

I dislike being on the bleeding edge for things. NodeJS is the most supported in the JS ecosystem. I find it much better to just be on the "default" option for things. You know, choose boring technology.

Normal_gaussian|6 months ago

For me - it doesn't support secure and reliable dependency vendoring.

The best way to do this atm. is using (and configuring) yarn for zero-installs.

This keeps dependencies inside the codebase so that: * Issues can be easily traced to the code that actually ran - development and deployment are the same. * Deployment doesn't depend on package repositories. * Deployment is secure from many kinds of attacks. * It is possible to transparently patch packages. * Development is only internet dependent when adding a new package. * and the best ease-of-use - no reinstall when changing branches.

theThree|6 months ago

I'm using Bun, but I avoid using any Bun.* function, because I don't want to bind my typescript project to a runtime.

silverwind|6 months ago

Node is much more stable than bun. bun has numerous crash bugs, something that is unheard of with Node.

hungryhobbit|6 months ago

Bun is still a toddler: it's not ready for primetime.

Simple example: you know how at the command line you can type "npm run", and then type a character or two, hit tab, and the appropriate script from your `package.json` will autocomplete? And if you keep going (eg. "npm run knex") you can do the same thing to autocomplete arguments?

Bun still hasn't figured out how to do that (https://github.com/oven-sh/bun/issues/6037), even though they can all but copy NPM's (already written) completions. I really liked using bun when I played around with it (and it ran my codebase perfectly, without issue) ... but if they can't handle something as simple as Bash completions, they're clearly not ready for the big leagues.

rovingeye|6 months ago

https://github.com/nodejs/node/issues/57215

Not supporting type stripping in node_modules is unfortunate

quectophoton|6 months ago

But... that's like half the reason why I wanted this feature...

Writing a library in TypeScript (with typechecks in CI/CD as devDependencies) and just importing it directly from Node.js...

spankalee|6 months ago

No, is the right call. TypeScript has breaking changes. It would need to be standardized first.

gdorsi|6 months ago

The best thing is that they are shipping this as "type stripping" which means that there are no sourcemaps involved, making it zero-cost in production!

Very well done Node team!

eashish93|6 months ago

But still, bun is the winner here. I recently started using bun and now not moving to node.js again. It just works.

lioeters|6 months ago

Yup, I'm in the same boat. Been using Bun for a year or so, and enjoying the many quality of life improvements. I still use Node when the project requires it, but I'm actively moving them over to Bun when I can. Still it's great to see Node continues to improve in its own way, benefiting a wider audience.

christophilus|6 months ago

Node still has a better repl, but I do agree. Bun is really good. It’s my goto, too.

tempodox|6 months ago

It looks like this works by stripping away the type information, so at best it saves you a transpilation pass and doesn't improve safety.

nine_k|6 months ago

One of Typescript's design goals is that removing all type-related parts of the source text should yield a valid JavaScript file. A typescript compiler does not generate code (unlike, say, PureScript).

You can run a typechecker (such as tsc) that check various properties of your code statically, relying on the type information. It is then erased.

The same applies, say, to Python: type annotations are ignored at runtime. Somehow similarly, Java's type information is also partly erased in the bytecode; in particular, all the information about parametrized types. (This is to say nothing about actual machine code.)

stevage|6 months ago

>so at best it saves you a transpilation pass and doesn't improve safety.

That's a bit misleading. Node being able to run TS code does not "improve safety", because that's not where the type checking happens. You can do type checking in your editor, or various other points in your toolchain.

Node being able to run TS code reduces the friction in writing TS code, which indirectly helps with type safety.

sdfhbdf|6 months ago

TypeScript never promised improving safety, maybe it’s a common misconception. But TypeScript has no runtime mode or information. You were always at the mercy of running and not ignoring the typechecker. Nothing stopped you from running ts-node or tsx on code with egregious type errors. TypeScript is more like a linter in that regard.

resonious|6 months ago

Not needing a separate compile step just to run some script sounds great to me. I will run tsc if I want a type check.

ryuuseijin|6 months ago

I'm using tsx for a project to achieve the same effect. As you said, it saves you from having to set up a build/transpilation step, which is very useful for development. Tsx has a --watch feature built in as well, which allows me to run a server from the typescript source files and automatically restart on changes. Maybe with nodemon and this new node improvement this can now done without tsx.

To check types at runtime (if that can even be done in a useful way?) it would have to be built into v8, and I suppose that would be a whole rewrite.

rand0m4r|6 months ago

I agree, that said if the main reason people use TypeScript is security they should use a decent programming language instead.

medv|6 months ago

Exactly! Type checking can be long and costly.

lbltavares|6 months ago

Yes, it can't parse enums, for example.

reactordev|6 months ago

Typescript mission is to strip itself of typing, what’s your point? Typescript will never be Java, you’ll never have runtime reflection.

mattlondon|6 months ago

Yeah agreed - saying "node can execute typescript files" is a bit misleading. More accurate would be "node can find-and-replace type information with spaces from .ts files and try and executing them as if they were plain JavaScript"

I suspect this would only handle the most rudimentary and basic typescript files. Once you start using the type system more extensively I suspect this will blow-up in your face.

It's kinda a shame. What a missed opportunity to do it properly (rather than relying on third party plugins etc)

Edit: if you are just using typescript for type checking at build time (i.e. the most basic rudimentary typescript files) the sure fine this may help you. But typescript also generates JavaScript code for missing features e.g. proper class access modifiers, generics, interfaces, type aliases, enums, decorators etc etc. typescript generates JS code to handle all that, so you're going to have a bad time if node just replaces it with the space character at run time.

gettingoverit|6 months ago

Even though this should be the way to go, it still leaves a bitter taste.

Remember ESM fiasco? Now we have a few years of that all over again, this time with different versions of TypeScript, settings, and tsgo. Good news!

shoeb00m|6 months ago

This is for transpilation only. it does not actually validate the typescript or perform any checks during runtime.

esm actually caused errors so there should not be any issues here

bakkoting|6 months ago

It also exposes a function which does type stripping (as `import { stripTypeScriptTypes } from 'node:module'`).

This lets you build simple web apps (i.e., those with no frontend dependencies) as pure TypeScript, including the frontend, by stripping the types out from your frontend scripts as you serve them: https://github.com/bakkot/buildless-ts-webapp

adityapatadia|6 months ago

This change finally helped our company move to typescript.

Just converted many of services to TS and some are WIP. It’s a big win.

primering|6 months ago

It's not able to execute TypeScript, but a subset of it. The claim in the title is misleading if not totally wrong.

This will unfortunately drive people towards using TS only as a linter, and not use its powerful features that are inherently impossible to implement with just type stripping.

bsimpson|6 months ago

I thought TS abandoned stuff that can't just be stripped. Besides enum, what do you use that isn't strippable?

seniorsassycat|6 months ago

> by replacing inline types with whitespace, Node.js can run TypeScript code without the need for source maps.

This is a really neat idea and I hope typescript adds this as a compiler option.

dingi|6 months ago

Contrary to the popular trend, I usually stick with Node and NPM for most of my projects. That said, I’ve run into plenty of headaches with CommonJS vs ESM quirks. Sharing code between frontend and backend (via shared libraries) still feels messy because of those differences. In one project I actually had to switch to Bun as the runtime, since Node kept failing at runtime, and strangely enough, Bun just worked without issues.

seniorsassycat|6 months ago

Anecdotally Ive noticed a lot of packages failing to build when updating from node 20 to 22.18.0 that weren't failing with earlier node 22 versions. .18 unflagged typescript support.

These are packages using ts-node or tsx to run typescript in node, and with node 22.18 they seem to be using nodes native typescript support instead, and failing due to its limited feature set, or subtly different module resolution.

827a|6 months ago

Its worth being clear that it isn't capable of actually executing TypeScript; it executes the "non-emitting" subset of the TypeScript language. If your app uses `enum`, among a couple other features, it wont work.

lerp-io|6 months ago

supposedly you can use --experimental-transform-types to enable enums but i never tried it so idk if does const enums correctly

tyleo|6 months ago

I’m curious what the benefit of stuff like this is vs tsc --watch and running the JS?

I’ve always just run tsc to a .gitignored’d directory and execute my JS from there.

Edit: Thanks for the responses. There’s some great examples in there!

wildpeaks|6 months ago

The benefit is one fewer step between Intellisense-enhanced development (which speeds things up with autocompletion and catches a lot of issues at design time already) and running the code, same as any linter.

bastawhiz|6 months ago

It performs no type checking, and you don't need to load the compiler to compile. Tsc is a heavy package and having Node do this for you means much faster startup.

As a note, Node has a built in --watch now, too

homebrewer|6 months ago

I've been using this for helper scripts, where each script is its own entry point, to assist with various maintenance tasks in frontend projects. Much easier to clone the project and run `./scripts/frobnicate` than faff around with tsc. Previously they would have been written in pure JS or just bash.

throwanem|6 months ago

It's faster and a good bit more convenient in greenfield, in my experience. Less safe in that you do still need a type checker, but nothing about Node's stripping is at all meant for more than experimental use anyway so that's fine.

ninetyninenine|6 months ago

Is there any movement in the node world for the runtime to be able to ascertain types? Obviously nodejs doesn't do this, but does Bun or Deno or are there plans for any of this in the future?

nosianu|6 months ago

This has nothing to do with the "node world". Such an enormous feature would have to go into ECMAScript. Which is very, very unlikely to ever happen, they may as well design a new language. All those runtimes implement that spec. Expecting them to write an extremely complex new feature that is easily more complicated than everything already implemented (especially with backwards compatibility, and given that the language was not designed for this) would be a bit much.

TypeScript is for compile time checking of a language that was not designed to have them. Runtime types have very different requirements! It has to be in the language from the early design phase, otherwise it will just be a hack with many conditions, restrictions and holes.

TS Types are only partially a description of the underlying types in the code, a very big part instead is that it provides guard rails that prevent you from using a lot of perfectly fine and valid JS code that would however be incompatible with type guarantees. You pay the price of using only a part of the large space of JS code possibilities for guarantees. If you were to put that into the runtime you would end up with two different versions of the language. If you still want to support the full JS you would end with two runtimes in one (or one that has so many branches and conditions that maintaining that runtime is a real beast).

ruined|6 months ago

i don’t think you’ll see this at the typescript level, and you won’t see anything like this that compiles to javascript. specifically, compiling typescript to javascript with runtime checks would not actually be very useful.

typescript is pretty ambiguous about a lot of the things that would need explicit definition for runtime safety, and anyways we already have tools for that - it’s called zod.

and comprehensive checks would incur a significant runtime penalty, unless they were restricted to external interfaces, which is what you’re really concerned about. we already have tools for that - protobuf, swagger, etc.

anything else is sharing a runtime with you. so either it’s in your ide, and you just don’t write shitty code; or you’re trapped in some kind of demonic javascript prisoner’s dilemma, and you are mutable.

so typescript is basically ‘good enough’ for developers.

thinking forward anyway, and assuming you’re really willing to share a runtime with a stranger…

node doesn’t really operate in that kind of context, but maybe browser code does. i could imagine a framework based on web components, workers, and maybe iframes, taking advantage of message boundaries to enhance analysis and conceal code generation. it’s not that much better than typescript.

but if you want efficient runtime checks, and you want to leverage static analysis and strong module boundaries to scope the type-checking codegen, and you probably need additional syntax, you might as well target wasm.

ireadmevs|6 months ago

I believe it would be too hard to keep up with pace of TypeScript development. We should probably at some point formally define the system and allow for alternative implementations outside of the control of Microsoft.

yahoozoo|6 months ago

What backend framework is the go to these days? Still Express?

ricardobeat|6 months ago

Express is still popular, but a lot projects these days use a full-stack framework like Next.js, SvelteKit, etc.

Fastify, NestJS (bleh), Koa, Hono are the modern replacements for express, none of them have caught on as a standard though. My personal favorite for small projects is Polka (https://github.com/lukeed/polka), when I'm not using Go instead.

norman784|6 months ago

IMHO the only reason you are using JS in the backend is because of some meta framework, otherwise is not worth. So at least for Nuxt is nitro, not sure for SvelteKit or the other React meta frameworks.

hungryhobbit|6 months ago

Next is where it's at these days in my opinion. You get a full-featured client-side React framework (the only one that supports modern React SSG), and then on top of it you get a better-organized approach to doing everything you can do with Express.

And do mean everything: I run an entire Postrgraphile server through Next (and you can easily do the sme with Supabase or a similar tool)!

prmph|6 months ago

I've tried it, still doesn't work as well as Bun's Typescript and test execution capabilities. So basically just went back to Bun.

EGreg|6 months ago

Great, it does type stripping by replacing TypeScript with whitespace. Can it also now load ES5 modules? That way we can use them for everything.

pjmlp|6 months ago

Quite for some time, assuming they are actually ES6 modules.

azangru|6 months ago

> Can it also now load ES5 modules?

ES5 standard (released in 2009) didn't have modules.

platevoltage|6 months ago

I actually just wrote my first node app in typescript without using tsc. It was refreshing. Hopefully browsers follow.

tills13|6 months ago

Just in time for tsc to stop sniffing the bootstrapping glue and rewrite in Go.

tln|6 months ago

Kinda wild this default got flipped on in a minor version. For that matter the TS stripping went in a minor version too

vivzkestrel|6 months ago

does it support watch mode? does it support path aliases?

wildpeaks|6 months ago

It supports the same things JS scripts can do because it's merely stripping type annotations.

asgr|6 months ago

Deno has sandboxing and much more - use it instead

1oooqooq|6 months ago

the js ecosystem is so sad.

all threads saying the truth, js on the server could implement actual ts and not yet another transpiler gets downvoted.

js "experts" think they are smarter because they know ts is just annotations for a linter. they don't even question why that is so and why that sucks.

thrown-0825|6 months ago

This would be a lot more useful in the browser, TS in node has never really been a problem for me and the config is pretty simple.

user3939382|6 months ago

I’ve been happy with JSDoc

yard2010|6 months ago

Bun can do it for years now. I think it's time to move on.

pinoy420|6 months ago

Bun has lots of issues and lots of incompatibilities with a lot of packages.