Integrating TypeScript into Node.js with Marco Ippolito === Josh: [00:00:00] Hi, and welcome to PodRocket, a web development podcast brought to you by LogRocket. LogRocket provides AI first session replay and analytics, which surface the UX and technical issues impacting your user experiences. Start understanding where your users are struggling by trying it for free at logrocket. com. I'm Josh, and today we have Marco Ippolito, Node. js collaborator and TSC member, here to talk about integrating TypeScript into Node. Marco, how's it going? Marco: Hey, ~great. ~Great. How about yourself? Josh: ~Well, ~I'm in a great mood because this is a very exciting topic. Could you just ~start us off, ~start us off with a brief overview of the recent updates in Node overall? Marco: Yeah. ~So, um, ~with the latest,~ uh,~ change, which has not been released yet,~ we, ~we added ~the, ~an experimental flag to Node. js, which is experimental strip types. allows users to,~ um,~ execute,~ uh,~ TypeScript,~ um, ~out of the box. So without,~ uh,~ external loader or the part dependencies,~ um,~ and yeah, it has some limitation, but I'm very excited [00:01:00] about this. It's a lot of work, but it's. like a major milestone for the project, in my opinion. Josh: A few clarifying questions on that. Would you be willing to explain to our listeners why you and I are so excited about this, why this is such a big feature? Marco: Yeah. ~So, um, ~like until now,~ uh, to, ~to run,~ uh,~ TypeScript, you would require,~ um,~ an external dependency. And so ~you, ~you needed to set up loaders. And ~like ~download some package from NPM. So like the developer experience,~ it,~ it was not the best. It could be improved. ~Um, ~so with this PR now, ~it's like, ~you don't have to do anything. You can just run node. file. ts, and it will work ~with, ~with some big limitations at the beginning, but ~we, ~we plan ~to, to make it, um, like ~to make it work seamlessly. And the project ~has tried, ~has talked about TypeScript integration for a few years. ~Um, ~so like it's finally here. Josh: It's finally here. You mentioned there are some limitations with it. Could you tell [00:02:00] us what are those limitations? Marco: Yeah,~ so, um, so one of the, like, the, the, ~the main problem with supporting TypeScript in Node is that Node ~has, um, is, ~has three release lines, all of which have a lifespan around three years. And TypeScript,~ uh, ~follows a very different release cycle. So TypeScript does not follow Samba. So this means that it does not provide enough stability guarantees for node to just be able to embed the TypeScript compiler in it. So we needed to look at different solution. And one of them is type stripping. So type stripping means you just ~like ~take inline types. and completely discard them without performing any type checking. ~So you try like ~you basically transform,~ uh, ~TypeScript into JavaScript code by removing everything that is,~ uh, ~ proprietary of TypeScript, but doing that, we also,~ uh, ~currently are removing some of the TypeScript features that actually need to be transformed into [00:03:00] JavaScript, such as an AMP site. Enums is not something that you have in JavaScript, but it's something that it's only in TypeScript. So at the beginning, we're not supporting any TypeScript feature that requires transformation, but ~we, ~we plan to do so, but it requires some technical work to do, especially in terms of source maps, because ~like changing the, like ~when ~you, you, ~you transform something, ~then ~you want to be able to debug it. And that requires additional work to be able to do it. Josh: Yeah, over on the TypeScript community side, a lot of folks, partially myself included, recommend trying to avoid features such as enums and older namespaces that kind of add new runtime syntax to the JavaScript world. This is, I think, a good example of some of the tricky complications that happen when you use those non JavaScript runtime features. Marco: Yeah,~ um,~ this is ~like, this is the ~one of the main reasons why ~we, um, ~we decided that it was okay not to support this feature at the beginning because ~like, those are, ~they're not deprecated. But, ~you know, ~it's not something that probably TypeScript wants [00:04:00] to do again, keep pushing things that survive at runtime. Just to clarify,~ we, ~we had a few meetings with the TypeScript team, so they are on board ~with, um, ~ we talked about some of the decision making,~ uh, ~ on the project, and,~ uh, ~we also discussed,~ uh, ~a few details ~and, uh, ~One other limitation that this feature has, that was also suggested by the TypeScript team, is that we don't support running TypeScript code in the node modules. So, ~you will, like, ~users will not be able to execute TypeScript only packages. And this is to avoid pushing the ecosystem to publish TypeScript files and not compile them. ~And, ~because if they ~so, ~it will just break everyone because of incompatibilities between nodes. between TypeScript's flavors. So this is another limitation, but I think it's pretty important that we added it. Josh: Sure. If someone were using a source syntax from a language that may be similar to TypeScript but is not actually TypeScript, something like Flow or Esno. Both of which are languages that, [00:05:00] similar to TypeScript, add in type annotations. Would this feature still work for them? Marco: ~Um, ~so like this feature currently does not work because it checks on the file extension to be a ts file. But if,~ uh, if like it's, ~it's a popular,~ um,~ superset of JavaScript, TypeScript. I don't see why we should not support it. ~Like, ~if Flow becomes extremely popular, and it is supported by SWC, which is our transpiler, I don't see why we should not support it. Josh: That sounds great. I'm not personally going to hold my breath. But out of curiosity,~ uh,~ FWC, great compilers. Are there a particular set of reasons why you chose it over TypeScript? The alternatives. Marco: ~So, um, ~The main reason why we used SWC is because,~ well,~ first of all, it's battle tested by Deno, so Deno is one of the main users of SWC, and second of all, because,~ um, other, um,~ other transpilers that we looked at required,~ um, to be Um, ~to add,~ like, go ~either the go,~ uh, ~tool chain with,~ uh,~ yes, build or [00:06:00] the rust tool chain with,~ uh,~ WC core, but also WC also offered,~ uh,~ wasm,~ uh,~ package. And ~so, like, ~in the first iteration, we decided to go with a wasm package because it's very easy to integrate. It's not the fastest in terms of. And has ~some, ~some issues, but ~like ~rather than integrating the full rust compiler into the project and like that requires auto work because I know that we support some,~ um,~ exotic architectures such as smart to us,~ um,~ IBM mainframe power PC,~ uh,~ like we support a lot of architectures that were not like,~ um,~ Most of them, Rust just added,~ uh,~ support,~ uh,~ like~ I ~think a few months ago. So we did that something that was not native, but it was plug and play and,~ uh,~ SWC was the best fit. Josh: Yeah. A lot of people who use note just on their own machines or for their own services might not realize there's an incredible breadth of places and forms that you can run note [00:07:00] in. So any feature added such as experimental strip types, it needs to roughly support all of them. Right? Marco: Yes. ~Um, ~it needs to run on all the machines and architectures that we support. And it's a lot. And so we need to be very careful when ~we, ~we add a new dependency, especially It contains a new language such as Rust. Josh: How does the note approach to TypeScript support compared to other JavaScript TypeScript run times like Dino and bun? Marco: ~Um, ~so,~ like, ~Node. js approach,~ uh,~ is very different.~ Um, ~as far as I know,~ um,~ Deno has SWC core, but also bundles,~ um,~ the TypeScript compiler for type checking. Um,~ um, ~the issue with Node is that ~we, ~we cannot do that. ~Uh, ~because~ We, ~we need to guarantee long term,~ uh,~ like assurance to our users and like the TypeScript compiler does not guarantee that. And also we need to like, [00:08:00] we decided not to vendor TypeScript directly. We wanted to go through a solution that could support TypeScript, but it could be extensible to also other dialects or like we want to see how. It evolves before locking in,~ uh, on, ~on something. So I think we started this conversation at least in 2022. ~Uh, ~there has been an issue with hundreds of. So like, ~how do we, ~how do we do this? How do we support this? And~ like, ~at the end of the day, the,~ like,~ the solution that landed was like, not to vendor TypeScript at all, but to provide a sort of compromise to make it stable, like for node needs, but also improve the user experience. Josh: So as an example of that compromise, you don't perform any type checking during transpilation at all. Right? Right. Marco: No,~ um,~ type checking is ~one of the, like, it's ~one of the features that are ~more, ~more [00:09:00] unstable in TypeScript because,~ like,~ they tend to break the way the type checking works within,~ uh, ~new syntax. So by not performing,~ uh, ~type checking at all, we guarantee that~ if, ~if there is no type checking, type check is not going to break and it's going to be,~ uh, ~at least,~ uh, ~more stable. And ~another like ~another thing that is important to note is that we decoupled the transpiler from the core. So the SWC is actually not in the core directly, but it's wrapped in a NPM package that's called Amaro, which means bitter in Italian. And so why did we do this? So that users can upgrade Amaro independently and so that if TypeScript releases a new version that contains breaking changes, ~like ~users can download it from NPM and use it without having to be stuck on the node bundled one. Josh: On the NPM package for Amaro, it's referenced, as you said, it means [00:10:00] bitter and Italian. It's a reference to Mount Amaro on whose slopes this package was conceived. Do you have any particular fun stories or descriptions of The details there, Marco: Yeah, ~I mean, I was, I was, um, ~ I was with my friends in the mountains of Italy and like my friends are also programmers. So we were like talking about ~like ~it was in the days where I opened the first draft PR and I was working. And ~like, ~it was very difficult to get consensus. ~So we were just like, uh, as we, ~as we looked out the window, we see this Mount Amaro and was like, Hey, I'm going to call this Amaro, which is Peter, because on how hard it is ~to like, to get this, ~to get this done ~to, ~to seek consensus. I was like, yeah, let's do this. And. It happened, Josh: ~it happened. ~One of the advantages that Node has compared to those other runtimes like Dino and Bun is that it's not coming first, that you've seen from experience how, say Dino, Bun, others have tried things out with TypeScript, some successfully, some lessons learned. Were there any particular lessons learned that you were glad to have learned before you started work on this? Marco: ~um, like, ~to be honest, ~I was, um, like,~ it's funny,~ but I was not really into, um, like, ~I'm [00:11:00] not an expert on other runtimes. ~I don't, ~I don't use them much. ~Like, ~I do almost my work on a node, but I was not even a TypeScript expert. ~Like, um, I learned most of it.~ Like, ~when I,~ when I learned about type stripping,~ um, and like, ~I didn't know how other runtimes were doing it. So ~I, ~I had a look and ~I, ~I noticed ~like ~some very good.~ Uh, ~the way that other runtimes handle, for example, ~the, um, like you in ~imports,~ uh, ~Dino uses the dot t s extension,~ um,~ instead of the standard dot j s that typescript, uh,~ uh, like makes you ~makes you use even to reference typescript files. ~And ~I think that was a very good idea because ~Um, like ~the extension resolution of the file should be done during development, not at the runtime. ~Like, ~it's not the runtime that should guess, hey, is this a JS file or this is a TypeScript file? ~Um, like ~that extension guessing should be done by tooling. ~Um, so that ~this is something that, ~yeah, ~I learned ~from, uh, ~from Dino and ~we ~We took back in,~ uh, in ~no JS. Josh: Thanks, Dino.~ Um, ~so thinking a bit more for the future. What are the next [00:12:00] few steps that you want to take with Amaro and TypeScript strip type support in Node. js? Marco: ~Um, ~so ~like ~the next step is to,~ like, perform the like ~finalize the decoupling of node and tomorrow. And like right now they live into separate packages, but Amaro doesn't do like it's just an internal doesn't do anything much than being an internal. So I want to make Amaro useful for users so that they can use it as a standalone package. So if they didn't need. ~Like ~to execute TypeScript from node, they can download Amaro and use it. And also what I would like to do is,~ um, ~add more features to Amaro. So for example, ~uh, type trans like~ TypeScript features transformation. ~Um, I like ~as a very far goal, I see also just config,~ uh, ~support and ~like, ~let Amaro become ~the, um, like ~the default TypeScript loader for node as a standalone package. But when it's bundled inside node, it behaves ~like ~in a very strict. Supporting the smallest ~sub ~[00:13:00] subset of TypeScript,~ um, ~so that users can opt in whatever behavior they want. Josh: There's been a movement in a lot of other tools. ~Uh, ~VTest has had TypeScript configuration files for a while. ESLint is actually,~ as,~ as we speak during recording, working on a pull. Do you see Amaro being useful to other tools that might want to embed some sort of strip type support directly in their config loading or other situations? Marco: Yeah,~ I, ~I think that is going to be ~the like ~the main use case of Amaro because if Amaro is compatible, also Node is going to be compatible. So ~like ~you can achieve the same. Level of compatibility and you can also add more stuff. ~It, ~it requires a ton of work. I'm not going to lie. It requires ~like ~a lot of work ~to be so, ~so that Amaro is on the same level of other,~ uh, ~loaders, such as TSX. ~Like ~those are already in the wild, they work well,~ uh,~ but ~we, ~we really want to have something that is,~ um, ~under the node org and does ~the, ~the TypeScript,~ uh, ~[00:14:00] translation. Josh: Do you think there'll ever be a future where you do explicitly want to be able to transpile TypeScript inside Node modules? Marco: ~Um, ~so ~the, the, ~the main problem is that if we allow users to run TypeScript in the node modules, like all the packages are going to be released in TypeScript. And that's a big issue because,~ like,~ different TypeScript flavors might not be compatible. ~Like, ~I use js extension in imports and the other one uses js extension in import and it breaks. So if we allowed that, we would create a lot of chaos in the ecosystem,~ uh,~ by not doing it,~ we, we, like ~we choose the most conservative,~ um, ~position for the ecosystem and also the TypeScript team told us like, Hey,~ don't, ~don't support not a TypeScript in the node modules, please don't do that. ~Uh, ~because they know very well that ~if, ~if it happened, ~it would break, uh, a lot of, um, ~Like it will break the ecosystem. Josh: Yeah, there is a humor points and interesting positivity that it's in part of the TypeScript team asking you please don't put TypeScript there, ~that ~that would not be helpful [00:15:00] for the ecosystem, even though that would have more of what they are producing in the world. Marco: Yeah. ~Um, ~I think ~like, ~I really enjoyed the meeting with the TypeScript team. ~Uh, ~they are publicly recorded so everyone can, watch them. ~Um, ~like the drive here is to improve ~the. ~The whole ecosystem ~like to, ~to make ~a, um, ~a step forward. So ~we, ~we were very careful with what we landed in this first iteration. ~Um, simply like~ for some people, it could seem like, Hey, ~they, they released like, uh, like~ this is not proper TypeScript support. This is ~like~ something. done halfway, but it really is not because we want to start from the smallest subset and move very carefully from there without breaking anybody. So ~like, ~there was a lot of years of discussion into this PR. Josh: Yeah, the naming of the option seems actually very precisely and well done. It's experimental. And also, you're not specifically referring to the general term TypeScript. It's specifically the types that you're experimentally [00:16:00] stripping out, right? ~Right.~ Marco: Yes. ~Um, ~so at the beginning I wanted to call it like just TypeScript support.~ We, we, ~we started the conversation and realized that,~ um, like ~if we were supporting TypeScript, we would support the full TypeScript features, meaning TS config, et cetera, but we are not. So we're not going to call this something that is not so strip types,~ uh, is actually like,~ it's exactly what it's doing. And there is no. like it's not misleading. Of course, we documented that. Hey, you could ~you can ~run TypeScript with it. But ~I ~maybe in the future we will support, I don't know, whatever language,~ uh,~ comes out. And we could also support the C 39 proposal for,~ um,~ for types type annotation. ~So~ Josh: It's all up in the air for those who haven't experienced the discussions, the very fun discussions around that proposal. What is the TC 939 proposal around types or type annotations? Marco: ~it's, um, like ~I didn't look into it very deeply, but it's,~ um,~ it's a proposal. It's still [00:17:00] stage one. So it's very far from being implemented, but it would add some type annotations in JavaScript files that,~ uh,~ the JavaScript engine could,~ uh,~ just,~ uh, like ~understand. and force,~ uh,~ these types into JavaScript. But as I said, like I'm not an expert and it's very far from being implemented, so I will not dive too much into it. Josh: Sure. There is an interesting parallel in that both of this, as you said, very early stage proposal for JavaScript and in the experimental strip types flag for node, they're both treating types as just comments as things to be ignored. There's no runtime type checking. There's no type checker integration. It's purely a development time helper construct. Marco: Yeah. ~I mean, this is, ~this is something like a key concept that I like understood. ~If, um, ~ if types are just something that are enforced, during development, then you [00:18:00] remove a lot of overhead from the runtime itself. So the least like operation that the random does, the faster it's going to be. And also all these rules that you choose to apply. Like you want to see errors being emitted. ~You want to like ~you still want to go through compilation. This is something you definitely want to do. You want to compile your TS file into JavaScript because,~ well,~ if you use JavaScript file, they're not going through the transpilation at run time. So it's still going to be faster. ~Like, so ~users should,~ uh, ~keep using JavaScript files. ~Uh, ~at the end of the day, it's Node. js,~ it's, ~it's for JavaScript. But if you want to test something, if you want to ~like ~check the integration with something with TypeScript, you can do that, but in production, you should still use JavaScript file. Josh: The characteristics, the description of what it is good or bad at runtime for production versus development time, [00:19:00] runtime, when you're just trying things out locally have so many differences between them, it must be very difficult to try to figure out these features such that they work for one and don't bog down the other. Marco: Yeah,~ um,~ like in this case,~ uh, well, the, ~the feature it's,~ uh,~ it's behind the flag. So like you need to opt in to enable it. ~Uh, ~but,~ um,~ we tried ~to, ~to make it so that it doesn't actually. ~Um, ~affect,~ uh,~ production. And with the fact that you cannot use,~ um,~ TypeScript in node modules, it's ~like, ~it really tells you, Hey, probably you should not~ like ~use this into your,~ uh,~ company production environment. ~Yeah. ~You should keep compiling in JavaScript, use JavaScript, but we give you the option to do that. Josh: I want to ask about something on the Amaro readme. There's a really interesting code snippet very early on under how to use that shows that the output of Amaro. transform. sync is actually the same string length as the input. You just replace the type annotations with spaces. And it mentions that as a result stack traces are preserved because it's the [00:20:00] same position in code. That's really clever. Nicely done. Marco: Yeah, this is,~ um,~ it's fun because ~like, um, ~this. ~Yeah. ~Came ~from, uh, uh, ~from Twitter. ~Like ~someone said, Hey, ~we tried the same, like ~we tried your same approach. And we found out that if you perform type stripping and you don't perform any transformation of TypeScript code. You can't just put white space instead, like when you replace something, you add white space, and we talked with the SWC maintainer, Donny, and like he said, let's do this, and we found out that it works really well, and this removes the need for source maps, ~so like, ~The fact that this,~ uh, ~pull request landed so quickly in terms of nodes. So in ~like ~20 days, it's also thanks to that, because ~we didn't, ~we didn't have to like, we supported the very small subset. We didn't need to care for,~ uh, ~source maps on like half flag, how to enable them and also,~ uh, the, ~the maintainer of the SWC ~was like it, ~he helped us so much. ~Like, ~he released,~ um, um, ~a [00:21:00] version specifically for Node, so that was very, very helpful. Josh: Are there any other cute tips and tricks that have gone into the Amaro package or its integration in Node? Marco: ~Uh, um, ~not really,~ like, we, we are more, ~there is more stuff that we are planning for the future. There is a lot of excitement and, like, how we can make the user experience even better than this. So ~there are, ~there are conversation going around the Node. js project, such as,~ um,~ configuration, having a Node configuration file. ~Uh, ~yeah,~ that's,~ that's still in the, some issues that are being discussed, but there is stuff that,~ um,~ Amado could benefit from. And~ like, ~I don't want to do any spoiler right now, but we will see. We will see in a few months. Josh: ~Well, that, ~that was going to be the next question. Or is there anything you are willing to spoil about new experimental stuff that might get investigated? Marco: ~Um, ~so like for TypeScript, the next,~ um,~ like the road map ~is, ~is [00:22:00] clear. ~Um, ~so there is nothing crazy in terms of like new features. Like we want to support transformation. We want to support source maps. ~Uh, ~we want to make,~ um,~ the,~ uh,~ SWC implementation native. So ~like ~add a bit of rust inside the C code base. Instead of,~ uh,~ wellsome.~ Um, ~but yeah,~ nothing,~ nothing crazy. I think like it'll be fun the way that,~ um,~ Amma will integrate with future,~ uh,~ no JS and how ~the, ~the future of no JS looks like. I think,~ uh,~ like there is a lot of work being done in the project. Like everyone is excited. ~Um. ~Like I'm very, very happy about it. Josh: Yeah, there's certainly a lot of excitement in and out of Node. The community is very much looking forward to no longer having to install TSX or TS Node or other to be able to just directly run TypeScript files in Node. So thank you for working on this. This is really good. Is there anything else you want to bring up or talk about while you're here? Marco: ~Um, like~ we were going to release,~ um,~ the experimental,~ um,~ strip types,~ uh,~ next week, probably because the release [00:23:00] was a bit delayed. So my advice is. Try to use it with,~ uh,~ like an open mind,~ like,~ given that this is like a very experimental, so there might be some weird failures and ~like ~the, there was some exploding,~ uh, like, uh, ~that could happen because, ~you know, ~it's ~the, ~the first time ~it's, it's, ~It's released, but ~like, ~instead of focusing on the limitations that this feature has being ~the, ~the first iteration, like I would like ~to, ~to focus on where this feature would go, like how ~we, ~we can,~ uh,~ we can make this better. ~Um, ~and also keep in mind that node is not the company node is,~ uh,~ it's all made by volunteers. ~So. ~Like we all do this in our free time while we have a job. ~So, um, ~like things don't happen magically. They happen because there is someone who's willing to sacrifice their free time ~to ~So ~like, ~this is something to keep in mind because I see a lot of comments say, Hey, but,~ uh, like, ~Hey, why don't you just do this? Why don't just do that? Hey,~ we,~ we, we need to like, it requires [00:24:00] some time. It requires some more effort compared to other projects that.~ Um, ~but eventually,~ uh, if, ~if it's a good feedback, it'll eventually happen. ~So,~ Josh: So let's say I'm a joyously happy ~and, ~and engaged user. I'm on the latest version of node. After this has been released, I'm trying out dash dash experimental strip types, and let's say it explodes. Or I have a brilliant idea for something that should happen. Where should I take these complaints or ideas to? Marco: ~uh, ~we created ~a, ~a repository called Nojs slash TypeScript,~ uh, ~that is,~ um, ~a space where ~we, like we have our, uh, ~we are setting up ~meetings, ~weekly meetings ~with also ~with the TypeScript team ~to, ~to keep ~like ~moving this. ~this~ feature forward. So if it's just a crash, you can open an issue on Node. js slash Node and report it. If it's an idea, if it's something that ~like ~you think we should really implement, or you want to implement it and you want a second opinion, you can ~just ~either join the meetings or open an issue on Node. js slash TypeScript on GitHub. Josh: Well, Hey, this all sounds fantastic. We've got [00:25:00] experimental strip types releasing very soon in node that we can try out. We've got some cool engineering under the hood, including the Amaro package with a. Wonderful reference as the name,~ uh,~ Marco, thank you for hopping on. This has been a pleasure. If people want to find you on the internet and chat with you in particular, is there anywhere you'd direct them? Marco: ~Um, ~yeah, on my GitHub, you can find,~ um,~ My name, Marco slash Ippolito on GitHub. And then you can find all the links to social media, LinkedIn, Twitter. ~Uh, ~it's all on my GitHub account. Josh: Awesome. Well, for LogRocket, this is Josh Goldberg. ~Uh, ~have a great day, everyone. Cheers.