
TypeScript is a statically typed superset of JavaScript that adds optional type annotations and modern language features to improve developer productivity and code safety. The TypeScript compiler performs type checking at compile time,
Loading summary
Josh Goldberg
TypeScript is a statically typed superset of JavaScript that adds optional type annotations and modern language features to improve developer productivity and code safety. The TypeScript compiler performs type checking at compile time, catching errors before code is run, and also transforms TypeScript code into clean standards compliant JavaScript. Jake Bailey is a senior software engineer at Microsoft where He works on TypeScript and and has made major contributions to the TypeScript compiler. Jake joins the podcast with Josh Goldberg to talk about TypeScript and his work. This episode is hosted by Josh Goldberg, an independent full time open source developer. Josh works on projects in the TypeScript ecosystem, most notably TypeScript ES Slint, the tooling that enables ES Slint and Prettier to run on TypeScript code. Josh is also the author of the O'Reilly Learning TypeScript book, a Microsoft MVP for Developer technologies, and a live code streamer on Twitch. Find Josh on Bluesky, Mastodon, Twitter, Twitch, YouTube, and dot com as JoshUakGoldberg.
Jake Bailey, welcome to Software Engineering Daily.
Jake Bailey
Hello. How's it going?
Josh Goldberg
Hi. It's good. I'm so excited to talk to you, Jake. We've done so much work and you've done so much work on TypeScript tooling. Everyone's has full what is your BlueSky description, your profile say?
Jake Bailey
I think my profile says according to Git Blame, I wrote the Typescript compiler, which is not true. But it is true if you don't bother to ignore all the migrations I made. Yes. Then I guess if you look at the new repo then it'll probably also say something similar.
Josh Goldberg
But there we go. Less so the one and only author of TypeScript.
Jake Bailey
No, no, no, no. Absolutely not. I'm new.
Josh Goldberg
Okay, well we're going to get into that. But before we go through all the tooling stuff you've done with TypeScript and the work on TypeScript in Go, which is incredibly exciting. Let's start with you. How did you get into coding, Jake?
Jake Bailey
A long time ago I was a child in elementary school and I had a TI73 and by some coincidence the teacher assigned an assignment which was basically I want you to tell the class about a button on the calculator. And I'm pretty sure I got assigned the program button. And so I had TI basic. And so my first programming language was TI BASIC in like, I don't know, the third grade, fourth grade. I don't know one of those. But a long time obviously. And then I just. It's one of those things where it's like I kind of think I knew what I was going to do from the beginning. And I think like in middle school I'm like, I'm going to check out the book on C and I just learned C. And then I was like, man, I'd really like to work on RuneScape private servers so that I'm just like modifying Java and like trying to convince my friends to install Hamachi so they can play in my private server and never succeeding. And then bubbles from there all the way up, right. And then I went to Illinois for computer science. Did that. It was great. And did lots of work otherwise. And I've been at Microsoft for, oh my gosh, what, six years now? Something like that. I don't remember how old I am most days, so remembering when I did something is always a challenge.
Josh Goldberg
Do you remember what it was that drew you to programming in the first place?
Jake Bailey
I have no idea, honestly. I just always been the basement computer guy. I think, like, quite literally that's where the computer that I had was located. So I'd just spend all the end of my days sitting, futzing around on the computer, doing stuff.
Josh Goldberg
So when you went into Microsoft, it wasn't originally for TypeScript, was it?
Jake Bailey
No, actually, not at all. In fact, it's funny, I had worked there the year before as an intern doing something completely different. Everything I did beforehand was like SRE work. I'm not sure if you're familiar with what an SRE is.
Josh Goldberg
Let's say I'm not.
Jake Bailey
It's like a site Reliability engineer. So it's kind of like people that work on infrastructure and support other teams and do all that kind of stuff. And I did that at like, you know, basically three different companies at that point. But then I was at Microsoft and then I was like, you know, I'd like to try to interview to see if I want to move over to Dev Div to do some programming language stuff, because at that point I was in the middle of my grad degree and I was taking so many programming language classes and I was just, I was like, this is awesome. And I'm like, let me go see if they'll hire me. And then I'm going to tell a longer story version of this. But like, they interviewed me and it was terrible because I got put through all the rounds and it was such a long day. I was so tired at the end of the day. And then I found out afterwards that because I did SRE work, my title was Service Engineer, not Software engineer. So they thought I had no programming experience and so they put me through extra steps that I didn't need to do. And then they apologized afterwards and they're like, oh, we're sorry, we didn't realize you were in a software engineering role. And I'm like, yeah, yes, I am. What the heck? So then I got hired and I was actually going to be on the C team working on the C compiler. At the last minute I get an email and says, hey, you're going to work on Python. And so I'm like, okay. And so I spent the first couple of years working on Python, so working on Python language server editor stuff. Later on Pylance, Pyrite. So Pyrite is a type checker for Python and I worked on that and then eventually I switched over to TypeScript, trying something new. And I've been on that for the past three years, something like that. I don't know, I don't have a calendar in front of me to tell me what I did, but I think maybe three years now.
Josh Goldberg
One of the nice things about Open source is that it's all in the public. After this we can go and see exactly when you started.
Jake Bailey
Yeah, yeah, yeah, absolutely.
Josh Goldberg
Do you remember what you were doing when you first joined the TypeScript team?
Jake Bailey
Ryan, our engineering manager, he assigned me a bunch of bugs. This is pretty common when people join the team, is that he'll sign them a bunch of bugs that are sort of like, get you started. And I had joined and Pyrite, if you're not aware, Pyrite is a type checker for Python. And Pyrite was inspired by the design of TypeScript. It improves on it in some ways. It doesn't improve it on some ways as well. And so I actually felt really familiar when I stepped over the line right to the other side and I started working on stuff. And so I think my first bug fixes were like fixing something with omit, like the helper type to do with private properties and protected properties, that was one of the things. And then working on parser bugs. So I think one of the first parser bugs that took me like three PR iterations was the parsing of arrow functions mixed with conditional expressions mixed with type annotations, where if you think about it, it's like arrows, but then there's also colons, but then there's also type annotations and it's really ambiguous, like what you're supposed to do with these. And that took a lot of time and it's been the same ever since. I modified it like three years ago. And so I'm assuming it's correct now, but I always get anxious when someone reports something or I think I saw Nicolo from Babel post something about these conditional expressions. I'm like, oh my God, please don't make me fix this another time because I was such a pain the last time to get it correct. Just like you take one TypeScript expression, you shove it in a JavaScript, it does something completely different than what you expect. Like, ah, right, the languages are different, right? And so that was one of the first things that I fixed. And then soon after I worked on the whole module migration stuff. That was like, pretty soon after I started on the team, actually like a couple months in is when I said, hey, Ryan, can I work on that? And I took over the work and we switched the whole code base to modules, which was the first time that I modified every line in the code base.
Josh Goldberg
Right, let's pause there. Actually, not all of our listeners are deeply familiar, as you and I Unfortunately are, with CommonJS versus ESM. When you say convert TypeScript to modules, first of all, what does that even mean? What's the context here?
Jake Bailey
So back in the days in the past, there was not ES modules. There was no specific syntax blessed by the committee that was like, here's what's in the language. And this was you parse. And so there are lots of competing formats for modules out there. I'll put out some acronyms, amd, system js, blah blah blah. And the one that node used was something called CommonJS. I say did use, it still uses called CommonJS, where you basically build your module a file up, but you put all your exports onto one object, and that's what you export and what everyone else consumes, right? And that's CJS. CommonJS is like import, export. The syntax written out, it's different, works differently, has different considerations. And it turns out that TypeScript used none of these. TypeScript predates ESM for sure. It predates many things. And so TypeScript was written in terms of namespaces, which by the way, were never called namespaces at the start. They were called modules. And in fact they were called internal modules. Internal modules, meaning that you wrote your code inside of modules which are namespacey, kind of like C or something, and you wrote your code in those, and then the TypeScript compiler would just take all those things and jam them together into one file and that was the output. It was like that for like a decade, something like that, like eight years or something. But what that meant was that we were stuck on Old syntax things turned out to be a lot slower because of it. And we had no idea actually. And we wanted to change over to modules, which required a big changeover. We had to change everything that was implicit into an explicit import, take all the code, unindent it. We had to rewrite a whole bunch of stuff, make it work, then add a bundler because now we aren't using TypeScript to put everything together again, stuff like that. So that was basically the big transform that I had done in that step, which was multi month process to finally one day run the script that does it because it was fully automated and just rip through it and then send a line, a PR that's like 3 million lines of code or something, right? And it worked out.
Josh Goldberg
So I actually want to ask you a question around this in the general context and framing of migrations of code or doing code mods and changes, because there are a lot of teams out there who have changes like this where their code base was written in year X with module or some style y. And then x5 or 10 or even 20 years later y is outdated and now they want to move it to Z. What are the kind of strategies or mental framings that you use when considering how to, or whether to migrate a code base that way?
Jake Bailey
So step zero, the thing to consider the best is just let's speak JavaScript. JavaScript is changing so much all the time. It's like that's the thing, right? And so the best way to solve this is to be preemptive and like, unfortunately keep up with it and try to make modifications in time as fast as you can and keep up to date so that you don't have to do a big migration. But that's obviously not possible from everyone, right? You need to have people dedicated to doing that. And so I think that the most important thing is to try and write tooling to do it, because if you don't, you're going to have a really hard time. And so the migration that I did that was entirely written using TSMorph, which is a project by David Sherritt that wraps around TypeScript and allows you to like modify code and, and turn it from one step into another. And so I basically was able to automate every single step of the entire process. And then if TypeScript changes, I would rebase the code base on top of the old one, right? And so the automation is really the reason that it was possible. The only way is to do brute force, which that works for me sometimes, but not when I had to redo the whole thing over and over and over again, trying to keep up with it. There's tons of interesting stuff inside that whole process, but not sure you want the little tiny details of that whole process. There's a whole talk about that somewhere.
Josh Goldberg
I think there's a lot of utility and understanding maybe the high level concepts. You mentioned ts morph. And because you work on TypeScript, you're well equipped to answer what does it mean to morph ts? Or I guess in this context, what is an ast?
Jake Bailey
AST is abstract syntax tree. Every language has this. It's like, here's your code, it's parsed out and it turns into a tree of language. Like, okay, that file is the top node. And then all the different statements inside the file are the next nodes down the list. And then you set up those, you say, ah, well, this is an if statement. And then you walk down and you have more stuff inside of those. It's like, oh, here's the condition, here's the body of the if statement, here's a call to another function, here's the name that you're calling, here's the parameters for that call. Oh, those parameters, those are also calls. It's a big tray that goes downward. And so when you're trying to write code mods, ts morph, all these different bits transformations, everyone has a different name for the same thing. You're taking the AST and you're just turning it into something else. You're doing like a rewrite from one thing into a different thing and making the code work the way you want it to work.
Josh Goldberg
And this is such a powerful technique for application migrations, right? If you have whatever thousands, hundreds of thousands of lines or files, there's no way you're going to be able to brute force a large migration such as CJS to esm. This seems like the only real way to do it.
Jake Bailey
Oftentimes it depends how much resolve you have. But yes, I would say automation. Sometimes you spend so much time doing the automation, it's not worth it, but usually not.
Josh Goldberg
So you accomplished one of the very Fun migrations of TypeScript from CJS to ESM. What were the benefits or what were the results of that?
Jake Bailey
Well, the headlining thing was that the whole thing got 30 to 40% faster. That comes down to really weird details to do with JavaScript. In short, because of those namespaces, everything was inside of an object. So if one file wanted to talk to another file's contents, it was actually a property access. It was doing A full, like object access to do those things. And so the entire time, even though everything looked like it was local, you never wrote those out by hand. You actually were spending 30% of the time just doing that. And so we ran the transformation and then I ran the code. I'm like, whoa, what the heck? This thing is way faster. That doesn't make any sense. Oh, wait, no, actually, this makes a lot of sense. Crap. And so like, you know, once we turned into modules, now, yes, they're separate files, they import each other, but a thing like esBuild or name, your bundler can understand that. So when they put that into a single file, it rewrites all the references to reference things locally. And then the engine is just like, well, that's just a static lookup. That's right here in Scope. And so that's really fast, right? So we gained a whole bunch of performance that way. So that's, that's performance. That's what matters, I think, to the end user, right? But for us it was kind of like, well, instead of running a full 30 second compile or something just to get an output JavaScript file, we could run esBuild. And that took like 100 milliseconds, right? So now anytime we start our test, it was like instant versus waiting 20 to 30 seconds to actually get it to start. That and other tooling that we were able to use that we weren't able to use before because no one used namespaces except for us. Basically. No one used the weird features that allowed TSC to merge the files together. Like TSC secretly was a bundler the whole time and no one actually used it except for us. And so we dropped that feature straight out of like 5.0 or something. Because we're like, yeah, no, this is an anti goal for us. We only preserved it for our own use and now we absolutely do not. We need it. Right?
Josh Goldberg
This portends things to come in two ways. One, the native code speed up. And also the improvements to TypeScript internally, enabling end user benefits such as performance, such as dropping deprecated features. But I want to switch topics ever so slightly for a bit before we go back to TypeScript, because TypeScript isn't the only large repo managed by the TypeScript team.
Jake Bailey
What is definitely typed, definitely typed, is a collection of type definitions for libraries that don't have them. So if you NPM install, I don't know, react, great package, right? It doesn't include any type definitions at all. TypeScript cannot read that and go, oh, yeah, there's an export called Use state, right? It doesn't have that ability. It can't analyze into the JavaScript code. Look past their bundling and minification to figure that out. And if it did, it wouldn't really be very useful. If React were written in TypeScript, they would emit DTS files and they'd have it, but they're not written in TypeScript. Right? And so definitely typed contains like some 8,000 packages worth of declarations that say, hey, this package exists, it has these exports, they have these types, and this is how you can use them, right? So when you install ypes React, suddenly you have all the types that make React work. And so you can say import react. And now you are off to the races, right? You could see everything in there.
Josh Goldberg
And how is this set up so that the packages are defined and then published?
Jake Bailey
Well, since its inception, which is again also 10 years ago, it was like thousands of individual folders with tsconfigs and basically a script that would scan them and see if anything changed and then package them up and publish them off to npm. I'm assuming what you're getting at here is the new form that I introduced maybe a year or two ago, which is maybe more familiar to people that have been worked around in JavaScript. And so instead of having a whole bunch of loose things with like a couple of package JSON files and then like really weird tsconfig options to sort of map them together, which then broke things in other weird, subtle ways, I made an effort to convert the entire thing to a PNPM monorepo. And so now all the packages link to each other as though they're actual packages, right? And so you PNPM install, and it takes a while in the current state, but like you'll link 8,000 packages together or whatever subset you want. And so now they're all linking to each other, declaring dependencies properly. We used to scan the files and figure out like, well, you mentioned node, so we're going to put node as a dependency of you, even though that maybe wasn't required at all. And so the new form is basically a bunch of packages. And then we take out the files that we want to publish, declaration files, put everything else behind, generate a package and publish it up to NPM whenever it changes.
Josh Goldberg
This episode is sponsored by Mailtrap, an email platform developers love. Go for fast email delivery, high inboxing rates and live 24. 7 expert support. Get 20% off for all plans with our promo code sedaily.
Let's pause there a little bit. And I'd like to go through a similar exercise as the migration before. Let's say that I'm on a team that has a large existing app that's not set up as a strict modern monorepo. And let's say that I really did want to migrate it over. What are the kind of techniques or tips you would give me in trying to migrate my existing app to a monorepo?
Jake Bailey
I think it really depends. The main thing is that it depends on your repo. Let me pull it out. There's a lot of people that have what is effectively a monorepo, but it's not broken apart strictly. And so they have imports that are like relative paths that are really long and they're actually supposed to split them out. You want to split them out into different packages, right? And so like, if you have the structure of different folders, you can create package JSON files in there, and then most of the package manager out there are totally fine figuring out where to find those things and map them together. I'll use PMCHM as an example, since it was. That's what DT does. You just make package JSON files and you put them in your glob and then it finds them and puts them together. And the real challenge is just breaking apart things so that the imports are no longer relative to somewhere else. You're actually working in a world where all your packages are kind of like they exist on NPM and they're already there. And so you want to refer to everything by their public names because that's what people. Also people are going to, you know, import them as. Or if it's an internal app, it doesn't matter quite as much. But now you can split up your packages into multiple pieces, build only certain parts of them. That's like a whole different monorepo setup configuration thing with TypeScripts or something, right? But splitting them up lets you declare the dependencies and you can better see what's in use by different parts of your application. It's mainly the big transform is just the import stuff that's the hardest bit, right? If you get them in the right folders as is, you can pretty much figure it out. And for TypeScript, that's a different thing where, like, we can pretty much figure it out no matter what you're doing, as long as you have the configuration file set up. So we're happy to do that, but there's like a whole bunch of stuff to talk about there in terms of like Project references and bits and bobs, but not sure what.
Josh Goldberg
Can we actually talk about that? Could you give us a Primer? What are TypeScript project references? Since you bring it up, sure.
Jake Bailey
So in TypeScript everything is in a project, so we'll start off with that. You make a tsconfig, you say, hey, here's the files in this project. When you call TSC with no arguments, it automatically finds tsconfig JSON, but you can pass a flag that says here's the project. And so it used to be that you'd have tsconfigs, you build them. If you split your program apart, you could do that. But it sort of doesn't scale really well for like I want to modify one part of my repo and then not rebuild all the other ones at the same time. And so a while ago, project references were added to TypeScript, where you can split your application up into different blocks that define like, okay, here is the front end, which depends on the common bit. Or like, here's my application, it might depend on the component library, but also the API types. And also those can be different bits and pieces across where when I rebuild my actual front end, I don't need to rebuild all the stuff to do with the API. I mean, that's just always there, right? Or maybe I don't need to rebuild the components if I'm just really, really the main front end or something. And so references allow you to split up your code into multiple pieces that declare different sets of files and then you can say TSC B so build mode. And then it will remember, oh well, this TS config doesn't need to be updated, it already built it and it will just skip it. And so that's a speed up in one way, but Also it allows TypeScript to figure out like, oh, when you go to def on something, how do I map that to the actual source file and not declaration output files or something. So it's all about like granularity and sort of editor use makes things better. Although there's always a limit where like some people try to make like 2000 project references and then you have a real problem because it doesn't scale that well that way direction. But they're pretty good for breaking apart your code base into different bits and pieces.
Josh Goldberg
Yeah, when people think of typescript performance, very often they think of the type system because it's incredibly rich. Someone got Doom running in it. Congratulations again to Dimitri from Michigan. But really, another aspect of typescript performance is splitting things up and setting up your project references so that it only has to rebuild some of the application parts that have changed when you save. I'm curious, do you find in production that when people come to you or the team with performance issues, it tends to be, let's say, more in the type system or more with the project configuration side of things?
Jake Bailey
Honestly, it's mostly configuration. There are people out there who have real type of performance problems, whether or not they are just doing something wrong or if they're intentionally trying to do something that is really bad for performance. Lots of really crazy type libraries out there that try to parse everything out of a string or something like that, which don't work very well in terms of performance sometimes. But most of the time it's kind of like, oh, whoops, I accidentally didn't configure the types property of my tsconfig and therefore it tried to load 1000 at types packages. Because unfortunately the default behavior of TypeScript is to load all those by default. And like, oops, that fixed like 90% of our problem. That's a real example that happened. Talking to a team about that. There's other places where it's like we have one project and it's massive and we just compile with once. Oh, you should probably try using references and see like if that helps because it makes things incremental. There's other teams that they have gone so far with references. They have 2,000 of them. And, and so now the fixed cost of having a reference is the dominator on the amount of time you're taking. And so it's like maybe have fewer projects. Do you really need one tsconfig per react component? Right. No, you don't need to do that hard.
Josh Goldberg
Right?
Jake Bailey
But it's usually stuff like that or they'll misconfigure stuff with paths, or they'll have a big paths array with path remappings that's like 100 lines long. And then we're like, oh man, we never thought anyone would ever have 100 of these things. And so the code base is just this linear loop that loops over everything trying to find them for every single file, right? And so it's like, oops, did I make a little N squared? Or whatever then, you know, whatever. Terrible algorithm. Oops, that wasn't intentional, right? That's like partially on us, but also like, well, maybe don't use paths or don't use the remappings or something. Like there's other better features through that same thing. A lot of the Cases are just like the tsconfigs were completely different between all of our projects and therefore TypeScript couldn't reuse ASTs. Whoops, didn't mean to do that. Like that's not good. Okay, we shouldn't even them out. Use a shared base config or something between some of our projects to help with that. That other bits. Like there's lots of. There's lots of stuff like this isn't a big contributor, but people will set their target to like ES5. Okay, so you're just spending a whole bunch of your mid time transforming your code into remove iterators or something or like Async await. Yeah, it's 2025. Maybe consider raising it because everything ever can run better. Stuff that's a minuscule portion, but there's lots of little paper cuts right where you just. Just keeps coming. It just keeps coming. Right.
Josh Goldberg
When we talk about typescript performance, there is of course the massive gopher in the room switching to Go. And I feel viewers would rebel if we didn't address it soon. So Jake, please tell us what's going on with Typescript in Go.
Jake Bailey
So for the past six months we have been porting TypeScript over to a new language. So TypeScript is written in TypeScript and we're hitting some performance walls that like, it doesn't seem it's likely they're going to be fixed in like a short period of time or they may be just inherent to the engine and just the language works. And we were like, okay, everyone wants us to reread everything in a different language. Okay, well we should try out and try porting some stuff over and see how it goes. And we did lots of trial and error, you know, trying different languages out. And so we ended up in a position where we ported all of our code over to Go. And so we published I think, what, two days ago, the big announcement of in the repo that has pretty much the entire checker ported. A lot of emit ported, obviously parsing and binding is ported. A lot of the CLI is ported. We have a language server implementation that does a couple things, but the biggest thing is just that through the port, through us using concurrency, parallelism, different perf tricks that were available to us in native code. We now have a typescript which is seemingly, I say seemingly, it is 10 times faster than just running TSC straight up. And there's people that they tested it on day of and they're like, hey, my company has this 3.5 million line code base. It takes us seven minutes to build our code and now it builds in 30 seconds and we're like, aha, that's a good speed up, right? Someone else was like, I have a 30 minute build, which, what the heck? But a 30 minute build. And now it's only, it's only a couple like few minutes. Like it's like seven minutes or something. Well, that's pretty good. I mean 30 minutes to seven minutes is incredible. But also what the heck are you doing? You have a 30 minute build. But I've seen projects, I have my personal projects on the side where it's like, I've been testing on them just to see what it looks like. And it can do the full compile from end to end, parse, spine, check the whole thing, emit all the files in less time than it takes our current TypeScript compiler to run the help command. It's pretty insane. It's pretty great. And I'm having a great time working on it.
Josh Goldberg
So that's incredible. 10 times faster. Just think of the CI bills that are going to be reduced by people spending less machine time running on this.
Jake Bailey
Oh yeah. And I think most of all we talk about end to end time. How often do you run a build? You know, not actually. Honestly, most of the time in the world on TypeScript is not spent running a build, it's in the editor, right? And so there's repos out there like VS code where it takes 20 seconds when you open up to the VS code repo to load their code before we actually able to start giving you like hovers. And because we're in native code, because we're doing parallelism, we are massively concurrent, like parsing out files and everything. And so now that thing loads in a couple seconds tops, right? That's like a big deal. We have people that it takes them like a 30 second or a minute just for their editor to start. Like on ridiculously sized projects like giant monorepos. And if we can do the same thing in a few seconds, that's a big deal. You can start, open up your editor and actually get feedback instantly. That's pretty good, you know. Yes, checking it's faster, obviously emitting is faster, but it's pretty great to actually be able to load your code in the first place. That's one of the biggest things I think that's in the blog post is like one of the first things we mentioned. APIs are the foundation of Reliable AI. And Reliable APIs start with Postman trusted by 98% of the Fortune 500 Postman is the platform that helps over 40 million developers build and scale the APIs behind their most critical business workflows. With Postman, teams get centralized access to the latest LLMs and APIs, MCP support and no code workflows all in one platform. Quickly integrate critical tools and build multi step agents without writing a single line of code. Start building smarter, more reliable agents today. Visit postman.comsed to learn more.
Josh Goldberg
Now there are some FAQs that have been popping up a lot and there are great answers already in previous interviews, in the live streams, in the discussions on the new repo. But for the sake of completeness I'd like to ask you some of the common ones that now just to get the high level overviews. Why can't you just make JavaScript faster?
Jake Bailey
Why can't we just make JavaScript faster? First off, they are making JavaScript faster. There's lots of proposals out and people working on things that will make the language faster and add certain features that could enable people to write faster code. Inside of JavaScript, there are just some things about the language itself which are inherent and I don't this is the design choice, right? But one of the things that JavaScript has is that there is concurrency, async await, right? But in JavaScript you're only doing one thing at a time. You're switching between maybe multiple tasks if you're using async await, but you're only doing one thing at a time. And so if we wanted to write a parser that's fully parallel, we can spawn up a bunch of web workers or something to parse a bunch of files at once. But all of those ASTs are often different threads and so we can't actually talk to other threads directly. And so we'd have to pull that data into one runtime and then use it. So like we can't make parsing parallel. If you're in a native language where you have shared memory concurrency, which is like the big phrase that's many languages out there, then you are able to parse in parallel and actually get all the results out, right? And so like that JavaScript is, there's proposals working on to do like shared structs, there's shared array buffers, right? Those are ways to share memory. But none of them are quite exactly the kind of thing that you would want to just like massively parse a whole bunch of stuff in parallel and access it directly. All of them require some sort of serialization kind of, you know, shared structs I think is the most likely to one to actually help with that kind of a thing.
Josh Goldberg
But it's not here and people are loading.
Jake Bailey
Unfortunately, it is not here for years.
Josh Goldberg
Till it is right.
Jake Bailey
And that comes down to engine optimizations and how that's going to work out.
Josh Goldberg
So, okay, you mentioned quite a few other languages the last couple years. Everyone has been seemingly writing all the new projects in Rust or the rewrites in Rust. You are not writing in Rust. Why go over Rust?
Jake Bailey
This is going to be a long list. I'll preface it by saying I really wanted Rust to work. I tried really hard to prototype stuff. I think we all wanted to make something work, right? And there's lots of challenges with Rust. There's the obvious ones that people think about immediately of like, I have to deal with the borrow checker now. Okay, you have to build the borrow checker. Yeah, like that's like, that's inherent to the language, right? Which adds an element of friction. But there are some fundamental things that are really challenging if you're working in Rust, which would really hamper our progress in trying to port code. And that's the main thing, is that we were trying to port the code. So all in all, TypeScript is a language without like a raw, like a specific specification of like, how it works. And so if we don't take this code and we don't port it one to one, it will behave differently and then some project will break and we'll be like, ah, crap. We don't know how to fix that because our structure is completely different and broken in some different way. Right. And so we wanted to port. That was the main thing, right? The prospect of like one to one, side by side, having exactly the same code for both of them didn't seem like it was very possible using Rust. And that comes down to stuff like cyclic data structures. So our ASTs, we mentioned before, our ASTs have things called parent pointers where you can walk down the tree and then look up and then walk back up the tree. You can go up and around, right? And so like, that's a challenge that's technically solvable. The Deno AST has a way to map those things by basically duplicating the AST a second time. So you have parent pointers, but extends further from that because you'll have like an AST with symbols, and those symbols will point to other symbols and those will point back to other declarations. And so you have multiple files that have like multiple declarations and then you're in the checker. And now you're building a type and the type is recursive and okay, so now the type needs to reference itself. And so it's like there are absolutely ways that you can tease out a different representation entirely and how to re implement these things. But it would fundamentally be like a RE implementation. And so the behavior would probably change in ways that we don't know how it would go. But also we would take us a really long time, I think, to like suss out all those details and get it complete. And so that really guided the way that we chose the language and prototype stuff.
Josh Goldberg
Sure. One last FAQ on this area. Anders also was a big designer and leader creator of C. Quite a few Microsoft and. Net area folks were surprised, perhaps more than surprised, that C was not the language. Why not C?
Jake Bailey
C is great. We have no problem with C. We love C. Obviously. Anders made C. There's no question about that. Period. There's so much code at Microsoft that's C. There's a whole bunch of great people working on that kind of thing. Absolutely. I think it just comes down to specific language details. And so I'll give an example. TypeScript is written synchronously. We actually don't have any async await at all in the entire thing. Maybe at the fringes to do communication with an editor or something, but it's all synchronous. If we wanted to make our code concurrent, how would we go across that? How do we approach that in C? The way to gain concurrency is to use async await. This is the same for. I mean, JavaScript has async await, I think, because C had async await. Right. Our code is written synchronously. But to put it in async await code, it'd be like a very different kind of transformation in Go. Like the concurrency model is completely different. Where you write your code, it looks synchronous, but you can be preempted. And so you sort of gain the concurrency for free just by orchestrating your code in a different manner. Right. And so porting the code in one to one like that is like a little bit different and may not have worked well as we might have expected it to work. That's a language example. There are other things. Like Nick Anders talks about the differences in the way that structs work. In the GO code base, we have lots of self pointers. We have self interior pointers. We take the address of a random property inside of a struct and you're able to do that. I'm not sure that C is able to do something like that. That could be changed in C, of course, but fundamentally C, their object model is different. There's lots of differences. I don't know if I could list out all the different positives and negatives for this. There's obviously positives like in C, there's obviously positives in Go in other aspects. Right.
Josh Goldberg
It's a fascinating study into. Even though a team might be more familiar with a particular language, the Microsoft folks are generally quite familiar with C. The project itself is fundamentally not built for that paradigm. It's a relatively lower level target. It's a compiler, it's a low level framework and area that just so happens to be quite well suited to Go.
Jake Bailey
I'd say that's true. C certainly has lots of AOT work going on as well. They're pretty different. And I think that there's also the aspect of Go is a somewhat established player instead of JavaScript because of esBuild. Rust is a big thing. Go is less of a big thing, but esBuild is written in that. And so that's also an important factor in how we. We choose a language. Right?
Josh Goldberg
Sure. Now that we've decided that you were correct to choose Go and no one should be yelling on the Internet about this, I want to talk about the transition a bit because you've mentioned you like doing large transitions at scale with semi automated tools. How has that transition been implemented internally?
Jake Bailey
I mean it's been going pretty well. Like there's a. There's a tool that allows you to take the code from the TypeScript code base, syntactically transform it into. Into Go, and then you're able to copy and paste functions at a time and they're mostly correct, they look about the same and then you just fix up the data structures so that they match what the actual implementation was.
Josh Goldberg
Why not use AI?
Jake Bailey
I tried AI, it works kind of. I think it's really a challenge because of like the amount of training data out there on compilers written in Go is limited to very few and especially training on code that is the TypeScript compiler. And so it works pretty well if you like. I definitely side by side of the code in like my editor with like Copilot and because it gives both files to the as context or something, it sort of figured out what I was doing and that helps me port some code. Absolutely. This was also like six months ago. Right. And things are always changing and the syntactic form was like the Easiest way to get there, in my opinion at least. Like to actually transform the stuff raw one to one. There's like tons of little tiny special cases that happen in our code base, only they wouldn't work elsewhere. The tool is not for people to use at all. It's sort of like an example. The code is really bad, right? But it works.
Josh Goldberg
You said this code is open source ts2go on your GitHub.
Jake Bailey
Yes, but don't look in the box too much.
Josh Goldberg
Understood.
Jake Bailey
It's a really long script with lots of little special cases that transforms the whole thing from one to the other using TSMORPH to walk the thing. Although it doesn't output using TS Morph, since obviously it's writing to go code.
Josh Goldberg
Let's say yet again that I am a general team who has a similar problem where I have a code base in one style or framework or language and want to output a similar or equivalent code base in another style or framework language. Are there any general tips or tricks you'd suggest that I look into before I embark on that journey?
Jake Bailey
Well, the first step is to think if you cannot do that, I don't know if I would. I wish this on anyone. It's like a big deal to like change from one entire language into a different tenant language. And so my first suggestion would be there are lots of performance tools out there to help you find things instead of languages that you're already in. So like avoid if you possibly can. I think that it comes down to sort of like, are you able to port code incrementally? So like, if you're using JavaScript and you want to write Rust, for example, there's lots of tools out there to allow you to like piecemeal transform code bit by bit using like nap ers or something. Right? That's one mechanism to do it. Now, is there an easy syntactic transform that you can make in that? I don't think that there's really is. JavaScript is very different than a lot of other languages. And so many things that are possible in other languages are pretty difficult for like, say JavaScript. Right. And so the transform that we made for like JavaScript to go, sure, it's like automated kind of, but it required a lot of thinking about how to structure things and how to fix things and go, ah, we need nil checks here and like how to like update data structures to be like the right format and add the visitors and all the different bits and pieces. And so that kind of transform is really challenging on say JavaScript between native languages is a little bit less scary because you don't have to worry about like, oh, JavaScript has optional properties. What compiled language has optional properties? Like, none of them. That's not how it works. Memory is laid out in a specific way. You need to put the things there or you don't. It's more challenging to deal with going from an interpreted language like JavaScript to something else between native languages. That's much more reasonable because you already sort of laid things out already in memory.
Josh Goldberg
That's actually an interesting point. First, TypeScript, back to it. Specifically, there are optional properties in the TypeScript languages. Internal and external representations of, say, AST nodes or options types. How does that work when you transform it over to Go?
Jake Bailey
There's a couple methods. You can either make different structs entirely for the different data structures. So like, this thing has something this one doesn't. And then you can implement like an interface or something to distinguish between them. Go doesn't have unions, so it's really difficult to like put those things in the same box. But most of the time you just end up with a field that's actually real there and it's just nil. It's like null or something like that. It's like spending the memory. And it's interesting because JavaScript is faster if you do it that way, because then the runtime can understand and produce the same code for both cases and you get faster. Those are transforms that we made in TypeScript a while ago as well, to fill out all the objects in identical formats, even if they contain undefined as properties. Because the engines like that better. Because people don't really want to deal with multiple different kinds of objects with different properties on them or not on them. The runtimes really like code to be like, really Even with the same objects with the same data, the same offsets inside of objects inside the Go code base. It's kind of just like if things are optional, there'll be a pointer and sometimes maybe it's like there's three things that all are set together or not set at all. We just make another struct for that thing. You just make a point of that struct instead. Right? That's sort of been the strategy for those.
Josh Goldberg
It's interesting that so few JavaScript projects have to deal with that kind of data normalization, avoiding the polymorphism in that way that it almost feels like a hint that, hey, if you're a JavaScript project dealing with that, perhaps you are more better represented as a go or Other lower level language project.
Jake Bailey
Yeah, depending for sure. Most applications this doesn't matter for at all. It's just that obviously we're writing a compiler, we are extremely CPU bound and we want to go as fast as we possibly can because it matters to a lot of people. Right. I think it's a little bit more forgiving if you're working in a browser where you're waiting for a render to happen or you're working on like a server where it's like, oh yeah, I really optimized this thing, but then I waited 100 milliseconds for Postgres. It's kind of like, well, you know, how much did you really achieve on that front? Right. It's really difficult, like we're in a. We're in a bad position to try to write a CPU powered like a really CPU heavy piece of code.
Josh Goldberg
We don't have too much time left. But I want to spend most of the rest of it talking about what you think is coming up next. Let's say that we got Typescript into Go. It all works, everyone's happy. 10x performance boost. Have you started thinking about what the implications of that are or what's coming after?
Jake Bailey
I'm really focused on making it happen. So past that I don't really know. We have lots of plans on the horizon of getting it into wasm because it's important for the browser. I mean, that's part of the job already. So maybe that's not something in the future per se, but the other thing that people are talking about is like, oh well, you're faster so you can do more stuff, right? And so can you add this feature? Can you add that feature? And it's like, maybe there's some features that we are thinking about for sure, but it's sort of like induced demand is the phrase that I've heard. If you make it four times faster, people will start doing four times more things than them. And then you're back to the square one where you were. And so I don't know what all we're going to add or what all we're not going to add. But there's some stuff I think that may occur. I think a lot of the time is just going to be spent getting it right because there's just so much use, so many users out there who need lots of different things. And I think we're getting the core stuff really well done. But there's lots of stuff out there like dealing with the API, dealing with the editor integration, dealing with Browser stuff that has yet to be seen how it's going to progress, but we're pretty optimistic about getting that stuff done.
Josh Goldberg
Well, there are a lot of open source projects out there who are waiting with bated breath to be able to use native speed type information.
Jake Bailey
Oh, really? I wonder who.
Josh Goldberg
Well, that's great, Jake. I have no more technical questions for you, but just one or two left. I'm going to ask you a question. You're going to correct me. What do you think is the greatest wad Al Yankovic song?
Jake Bailey
Yankovic? I feel like it's like Yankovich is when you're. When it's the year 2006 and you're downloading a song off Limewire or something. Right. And they've misspelled the name. But I have to choose Albuquerque, right? You gotta choose Albuquerque.
Josh Goldberg
Why?
Jake Bailey
I don't know if I can explain that. It's so long. It's got lots of lyrics you have to memorize. It's just a good one. I don't know. That's a good one.
Josh Goldberg
What for you defines a good A Weird Al Yankovic song. Or rather what for you is the appeal of Weird Al Yankovic?
Jake Bailey
What's the appeal? Oh, God, I don't even know what I'd say. What makes a good song is what. I don't know the original song at all. If the entire thing has been wiped from my brain. Because that's. His version is better, you know. There you go. Right? Oh, that's. That's more memorable for me. For me, the original songs. I'll be original. Obviously it's not. It's not. That's not a parody of something. So, you know.
Josh Goldberg
Well, Jake, you've been a trooper. Thank you so much for hanging out, tolerating the Weird Al Yankovic question and diving so much into the world of typescript. We talked about how you got started, what typescript looked like internally before you got around to switching to modules. And now as part of the big migration, switching to Go. There's all sorts of great stuff happening and it's really exciting to be a web developer these days. Last question. If there was anywhere on the Internet you'd want people to go to find out more about you, your work, typescript, are there URLs you'd suggest they look into?
Jake Bailey
Jakebailey.dev. that's the main thing. It links to everything else. It's also the same username on bluesky, if you want to see me there. That links to everything. So if you go there, you'll find everything else. My GitHub, everything else. It's all Jake Bailey. Like one word.
Josh Goldberg
Excellent. Well, thanks so much, Jake. This has been fantastic. So for Software Engineering Daily, Jake Bailey and Josh Goldberg. Thanks for listening, everyone. Cheers.
Jake Bailey
Cheers.
Josh Goldberg
SA.
Podcast Title: Software Engineering Daily
Host: Josh Goldberg
Guest: Jake Bailey, Senior Software Engineer at Microsoft
Release Date: July 15, 2025
In this episode of Software Engineering Daily, Josh Goldberg welcomes Jake Bailey, a Senior Software Engineer at Microsoft who has significantly contributed to the TypeScript compiler. Jake discusses his journey into programming, his role at Microsoft, and his deep involvement with TypeScript tooling.
Jake Bailey [01:37]: "According to Git Blame, I wrote the TypeScript compiler, which is not true. But it is true if you don't bother to ignore all the migrations I made."
Jake shares his early experiences with programming, starting from elementary school with TI BASIC. His passion led him to explore languages like C in middle school and eventually pursue a degree in computer science at the University of Illinois. Jake recounts his initial role at Microsoft, which was unrelated to TypeScript, focusing instead on Site Reliability Engineering (SRE).
Jake Bailey [04:00]: "I was in the middle of my grad degree and I was taking so many programming language classes and I was just, I was like, this is awesome."
Initially slated to work on the C compiler, Jake was redirected to Python-related projects due to a misclassification of his role. After contributing to Python language server projects like Pylance and Pyrite, he transitioned to the TypeScript team, where he began addressing critical compiler bugs and spearheading major migrations.
Jake Bailey [05:56]: "Ryan, our engineering manager, he assigned me a bunch of bugs. This is pretty common when people join the team, is that he'll sign them a bunch of bugs that are sort of like, get you started."
One of Jake's significant contributions was migrating the TypeScript codebase from CommonJS (CJS) to ECMAScript Modules (ESM). He explains the complexities involved in this migration and the performance benefits it yielded.
Jake Bailey [13:32]: "The headlining thing was that the whole thing got 30 to 40% faster... Because of those namespaces, everything was inside of an object."
The transition to ESM resulted in substantial performance improvements. TypeScript's compile times decreased dramatically, and tooling integration became more efficient.
Jake Bailey [15:32]: "Instead of running a full 30-second compile... we could run esBuild. And that took like 100 milliseconds."
Josh inquires about strategies for migrating large codebases, to which Jake emphasizes the importance of automation and incremental transformations using tools like TSMorph.
Jake Bailey [10:42]: "The most important thing is to try and write tooling to do it, because if you don't, you're going to have a really hard time."
Jake delves into TypeScript project references, explaining how they allow splitting a codebase into manageable projects, thereby improving build times and editor performance.
Jake Bailey [20:33]: "Project references were added to TypeScript, where you can split your application up into different blocks that define like, okay, here is the front end, which depends on the common bit."
A major highlight of the discussion is Jake's project to port the TypeScript compiler from TypeScript to Go, achieving a tenfold increase in performance. He details the challenges faced during this transition and the impressive results obtained.
Jake Bailey [25:44]: "We published... the entire checker ported. A lot of emit ported, obviously parsing and binding is ported... it's 10 times faster than just running TSC straight up."
Jake explains why Go was chosen over Rust or C for the port, citing the language's concurrency model and ease of one-to-one porting without altering the compiler's behavior.
Jake Bailey [31:50]: "TypeScript is a language without like a raw, like a specific specification of like, how it works. And so if we don't take this code and we don't port it one to one, it will behave differently and then some project will break."
The conversation touches upon common performance pitfalls in TypeScript projects, such as misconfigured tsconfig files and excessive use of project references, and how to mitigate these issues.
Jake Bailey [22:58]: "There's people out there who have real type of performance problems... 'I accidentally didn't configure the types property of my tsconfig and therefore it tried to load 1000 at types packages.'"
Looking ahead, Jake discusses the potential advancements enabled by the enhanced performance of the TypeScript compiler, including integrating it into WebAssembly (WASM) for browser environments and accommodating increased feature demands from the developer community.
Jake Bailey [43:39]: "We have lots of plans on the horizon of getting it into WASM because it's important for the browser."
In a lighter segment, Josh asks Jake about his favorite "Weird Al" Yankovic song, leading to a humorous exchange about memory and musical preferences.
Jake Bailey [45:24]: "I don't know if I can explain that. It's so long. It's got lots of lyrics you have to memorize. It's just a good one."
Jake wraps up by providing resources for listeners to learn more about his work and TypeScript contributions.
Jake Bailey [46:30]: "Jakebailey.dev. That's the main thing. It links to everything else."
Automation is Crucial: Large-scale migrations, such as moving from CommonJS to ESM, benefit immensely from automated tooling to handle the complexities involved.
Performance Enhancements: Porting the TypeScript compiler to Go has resulted in significant performance gains, enabling faster compile times and improved developer workflows.
Effective Project Management: Utilizing TypeScript project references can optimize build processes and editor performance, but requires careful configuration to avoid performance bottlenecks.
Future Innovations: The ongoing improvements to TypeScript's performance open doors for further enhancements, including integration with WebAssembly and meeting the growing demands of the developer community.
Community and Open Source: Jake's work exemplifies the collaborative spirit of the open-source community, underscoring the importance of public contributions and shared knowledge.
For more insights and updates on Jake Bailey's work with TypeScript, visit jakebailey.dev.