Typescript Performance: Going Beyond The Surface with Aleksandra Sikora === [00:00:00] Hello and welcome to Pod Rocket, a web development podcast brought to you by Log Rocket. Log Rocket helps software teams improve user experience with session replay, error tracking, and product analytics. You can try it for free@logrocket.com. I'm Noel, and today we have Alexandra Sura joining us. She's here to cover her latest conference talk, TypeScript performance, going beyond the Surface. She's an open source engineer at the Guild and has previously been the lead maintainer on blitz dot js. A tech lead for the RA console. Welcome to the show, Alexandra. Welcome everyone. Thank you for having me here. Thank you, Noel, for the introduction. Of course. Of course. Yeah. So I guess before we get into the talk, can you just give our listeners a little bit your background and experience in web dev and what you've been working on recently? Yes, of course. So I originally started as a backend engineer. At my first two jobs I was a Python developer and then a go developer. But in both of those jobs I moved to the to be more like in a full stack role [00:01:00] because I really liked frontend. I also figured in my second job that I really like working on dev tools and I would like to work on open source. So this is why I applied to Hara and I moved my way to, working on the tooling more. And yeah, and this is where I stayed. Like I, after Hara, I was, Maintainer of a framework, and currently I'm doing open source for the guild, mainly in the GraphQL ecosystem. And I was like, going from the backend to the front end in the web ecosystem. That's that's a bit of an interesting journey, particularly languages wise. I feel like a lot of developers don't go from a language like go and then, start spending a lot of their time in JavaScript. Do you, I guess throughout your career, have you always been working on multiple languages or do you really focus on one at a time? Yeah. Yeah. I consider myself quite a polyglot. This is what people say about me. I think this is because of the university. I started computer [00:02:00] science and they would give us like assignments. To write an interpreter or a compiler or like something like super difficult in a language we've never heard about before. Like thanks to university, I got used to working with new languages, so you can throw a language at me and I should be able to pick up writing in this language pretty fast. And maybe the older I am the slower it is, it's still I think languages are very interesting to me in general. Yeah. No, I feel that it's like the context switching always just seems to get a little bit harder all the time. Yeah. But Yeah. you. I'm with you. Yeah. But let's focus on, TypeScript a little bit. So,, you frame your talk talking about why we use scrape TypeScript and it's utility. I think a lot of devs probably have a pretty good handle on like the general, justification for using it. But can you kinda tell us about like the, how performance plays into TypeScript, like how we should think about Typescripts impact on our running code? Yes. So usually when we're thinking about performance, [00:03:00] we think about the end user performance, like how fast things are , for our clients, how fast our applications are when we open them in the browser. And I feel like we don't talk that often about the development performance, like how fast we can develop features. How fast we can deliver value for the clients. And I had this metaphor in my talk about TypeScript that is kinda like being a Formula One driver when the runtime performance, it's like when you're actually on the track and when you are , Driving, you're on the track. This is like the runtime performance, like how fast you can go. But then you are at the pit stop. They're changing your tires. They are getting you ready for the next part. Of the race. And this is what I compare to the development performance, like , our tooling performance because we have to be like up to speed. Like our tooling should slow us down. We shouldn't spend [00:04:00] like half an hour on the pit stop because that would kinda break the point of, doing the race. This is, I think why it's important because, every time you have to wait for your compiler to compile the code or for editor to, give you the information about the types it can be quite annoying if that's slow. Did you ever have those moments when you know, your VS code or your other editor crashed during the development and you had to restart it? Yeah. It's been a while, but yes, I have. I guess is there anything devs can do to or should be keeping in mind, I guess to that end, to make their kind of dev experience easier when using TypeScript? Any pitfalls, anything to watch out for? Yeah. So when I was like researching this topic, I found some ways to like debug the performance. Firstly, I wanted to say that, TypeScript team did a lot of really amazing work to, to make the types of compiler fast. Like it's much better than it used to be, like few years ago.[00:05:00] We're not starting from like a bad position. It's all day. Debugging or optimization stuff that we can learn about. It's not like we'll use them on daily basis because, as you said, it's been a while since you had your editor crashing when you when writing code because TypeScript actually got much, much better. But it's there are those rare moments when it is really annoying, when something is really slowing you down. When your compiler crashes, when your editor takes. Forever. And this is when this knowledge, having this knowledge pays off. And I think this is, even if it happens to you, you like once a year, it's still worth knowing how to proceed when that happens. Gotcha. Gotcha. So is there I guess in your talk you talk about you cover some TypeScript horror stories. Are those horror stories related to these kind of dev time crashing crashes or just like bad code that have led to this? Or what are the horror stories specifically? Yeah. So one was [00:06:00] from few years ago there was this issue with TypeScript when you had object spreads, a bunch of them. And then when TypeScript tried to figure out what's the type, it was produced like a real, really huge union. And it would resolve the compiler to go with out of memory error. And, I think I first encountered this error in my second job and I was still pretty new to TypeScript to like the web development world. And I had no idea what was going on and I had no idea how to debug this and what to do about it. Yeah, that wasn't fun. And it actually took me a while to narrow this down. Like what the fact that compiler crashes to narrow it down to the object spreads. So this is one, one example, and it actually got fixed also a few years ago. They are now like producing simpler. Result types instead of like big unions, they are like merging the the types together. So it's it's actually, it's not an issue anymore. [00:07:00] And I also remember when I was working on the framework the BL js framework, we got a bunch of issues reported with slow. Editor support because there was a lot of generation going on. There was result for for the validation, and there were types generated by Prisma. , there were a lot of generics being used and all of that was slowing TypeScript down and Yeah, and I remember that was the moment when I was like, okay, so I really have to learn more about the bagging performance and what to do because, if I get to work on the framework and solve those kind of issues, I need to know how to proceed and how to even get started with looking into those issues. Yeah, do. Do you think that Most devs should pursue trying to get a better grasp on like the the compiler the, what's going on with TypeScript kind of under the hood. Or do you think [00:08:00] that devs like, have an okay grasp of that? I guess is there do you think that people should be carrying more of that with them to help them debug these issues? Or would you recommend they can just do their best focus on their code, see where I think it's, yeah, I think it's useful to know what's going on under the hood because then when you have to debug those issues, you kinda it can, it kinda makes it easier to pinpoint where exactly is the issue. And also when you see some errors, like maybe not even related to performance, but when you see errors that TypeScript shows at you. Can also know what compiler steps are there related to, and that can be useful information whenever you are, debugging or dealing with some issues. So how do you recommend Des that kind of haven't really done this before? Say they're, they've been JavaScript doess a while, they're good at debunking JavaScript code, but then, they're like the type script compile time, trans pile time is like a kind of a different beast. How do you recommend [00:09:00] they, start getting into that to figure out like when they have these complicated errors or something's crashing how they can start picking that apart. Yeah. So there are a few flags that uh, types of compiler has that are quite useful. Like usually how I start is by running diagnostics. There's this diagnostics flag and then it gives you this output with How much time, particular steps of the compilation took and also gives you some information about like the lines of code that are being processed by the TypeScript compiler. And that's, in my opinion, a good starting point because for example, you can see that, oh, your check time is taking 10 seconds, which is way too much. Then you know that, okay, the issue is with the type checking itself, or maybe there will be an issue with rate, time, or like right time. Then you'll know that, okay, maybe the issue is with misconfigured. TypeScript config [00:10:00] and with the exclude include or file settings because, maybe TypeScript is picking too many files that aren't needed to be picked up. So yeah, this diagnostic flags for sure. This is the the best first step. There's also extended diagnostics, which is like an extended version. You, you'll get to see more information, especially about the lines of code. Gotcha. If devs are just like using their i d E is there a way for them to go enable those flags? To get, just have the, like IntelliSense and VS code do that for them? Or are they gonna have to go to the CLI to get the, this kind of Yeah. They will have to go to the C l I and run like for example, yarn, T S C minus, minus extended diagnostics. It will, it'll give them the output. Gotcha. Gotcha. Yeah, that makes sense. I'm curious, like kinda code you've encountered in the wild, how healthy do you think most of the, like TypeScript code you're [00:11:00] seeing typically is? Do you find that it's usually like in a pretty good state or are there a lot of projects that like, have a lot of optimizations that could be done? In general, I think as long as the project is using the latest TypeScript, it's in pretty good state because as I said before like, there were a bunch of improvements especially in the recent months down to the TypeScript compiler. So, You should always keep the TypeScript up to date. And when I was preparing this talk, I was like randomly opening some repositories, some projects that were written in TypeScript and I was running like the extended diagnostics or I was doing some other performance, the bagging on those projects to see, oh, maybe I can find something. Interesting there that I can show during my presentation. And I have to say honestly, that a lot of them were in a really good shape. I found some that had a few complex things going on that could be improved, some [00:12:00] needed bigger refactor for example. There was some issues that Like the whole architecture was a bit complex. In order to optimize types of performance, which was really slow they would need to rethink their architecture of the application, how they are dealing with certain things. So it's not always fixing one type or removing one type and replacing it with something simpler. Sometimes it's a matter of, making a larger. Refactor. But yeah, in general, I think it's it's like the applications that use TypeScript are in a good shape, but if there is an issue, it's usually significant issue. Yeah. Devs probably know when they have a problem most of the time. They're feeling the pain points. Yeah, I guess I am, I'm curious to that point of a project needing refactors to clean up. The types make TypeScript faster. In those cases, do you typically get a sense just even just looking at the code, that it, those sections were overdue [00:13:00] for a refactor anyway? Are there ever, have you encountered cases where it seems like the. The general pattern or the shape of the code is pretty healthy, but even despite that, like the TypeScript pilot compilers struggling or there's some, something that is making that process slow for devs. And in those cases, if you have encountered any, is it ever hard to justify like making functional code changes just to make the type system behave a little bit more cleanly? Or is it typically the case that like if there's one, then there's usually some kind of code smell anyway. So one example I found when I was like going over different projects was with T R P C and T R P C routers. There was one project that used they, they had a really complex architecture. They had a bunch of routes. They were merging them and they were like many levels of those routers. And, T R P C is doing like a lot of inference for us. It can, it can give you this fully type safe client out of [00:14:00] the procedures that you declare in the T R P C router. There is like already a lot of inference going on, which as long as you have like simple router it's working really well. But when you kinda complicated it to the point that t r PC gives up, like not maybe even not t r pc, but TypeScript in general, like the inference is like too much. Then maybe, you have two options. Is. Tool Liket, R P C is still enough for you. Maybe you want to move to something else. And T R P C is just an example, like I, Lovet, RRP c I think it's a great tool. Or maybe you can. Use other interfaces. Maybe you don't have to merge those routers. Maybe you can help TypeScript with with the inference. Because from what I remember, I saw those issues that, that the types are too complex to calculate. There was this errors there, there was like literally no type information about the about the structures types.[00:15:00] Yeah. Is it a code small? The code itself works really well and it looks nice, but what's going on in the type system, it's like another thing. It depends what you categorize as a code small. Yeah. Yeah. I guess maybe just to help contextualize this just a little bit, for devs that have used transcript a little bit, but they've never. Like done any of this com complex type declaration. Maybe they're just not sure what is and is not like a complex type. Can you frame that a little bit? , how are these complex types usually coming to be? Is there like lot of generics that are going on or Programmatic co-generation that's leading to them? Or what's yielding these types that are kinda hard for the compiler to figure out? Yeah. I think one, one example would be with generics when task group has to infer a bunch of things and there are so many possibilities. How they define a type is going to look like, for example, let's stick to T R P C. Yeah. For the client to have all the type information it has to infer those types from the from the T Rrp C [00:16:00] router when you declare all the procedures that are available and you also declare what are argument these procedures, what are the test group also has to infer what are their return types. So there is a lot of inference going on, a lot of work for TypeScript which will be working like very well. As long as you don't push it like out of it limits, as long as it's not like large. Another example would be with like conditional types when you have a bunch of layers of conditional types. So there are multiple puffs for TypeScript to take and maybe something else that I saw in a few repositories and in a few projects are string. Tarot templates when you kinda try to put everything in there and you end up with unions of, of strongly terrorists that have like thousands of items inside. So this. It's also like a lot of work for TypeScript because when, [00:17:00] whenever it has to like, compare two types and see if two types are assignable to each other, it has to compare one type to all of the elements of the union to see if there's a match. So that's also something that causes a lot of complexity. Yeah, so I guess when devs have these complex types or there, there's something slowing them down, are those typically fixable using just like strictly changes to the projects type system? Or are there actual code changes that often need to be made as well to fix these kind of slow type generation problems? I think it's both, but very often it's it's more about. Types, ref factoring. You can make some changes to the types to make them simpler. For example, with a string with tarot templates you can ask yourself like, do you actually care about having this huge union of all the options? Or maybe typing something as just a string is enough for you so that TypeScript has. To do way less work. [00:18:00] And another example is that when you have a type that's like really complex, maybe there are some conditional types. Inside maybe there's an option to extract the complex part of this type to a separate type S For example, you have one type S with a lot going on. You extract something that's inside to the second type alias and you just use the second type audience inside of the original one, and it can also help TypeScript with the compilation and type checking because now when it. It doesn't have to calculate one huge type every single time you use it. It has the complex part extracted. So it calculated it once and it just relies on it. So it's like those kind of changes, like extracting complex parts to separate interfaces or separate type. Osis can also help types group, be faster, [00:19:00] calculate things only once. Yeah. Gotcha. So how I feel like when we're when thinking about types in this way, almost begins thinking about like the type system as its own, maybe like language. It's like its own runtime that needs optimized and thought about, like we would typically think about the execution at execution time for our code. How do you recommend devs kind of start thinking about. This way. I know we got into it a little bit before understanding the pieces. But like how would you recommend devs dip their toes? And if they're not maybe experiencing this acutely, but they want to have a better grasp on how these types are generating, how they can make sure that they're, the types themselves are well optimized? I would recommend using a generate trace flock so you can run, for example, like yarn or mpm, run tsc minus generate trace. Then you provide an outward directory, let's say. Trace and it will generate two files for you. First the first file will [00:20:00] be trace json and the second one will be types json. And basically then what you can do is open performance tab in your browser. You can load the trace dot json file and you'll see all this. Trays visualized, and then it'll give you an overview of what's going on in your project. So you can zoom in, zoom out. You can you can investigate certain parts and whenever you notice something unusual, for example there's one file that is taking way more time to type check down other files. Then you can dig into it and like slowly narrow the issue down. . , so say, say devs are they've been on this journey for a while, or even they're just starting and they're working on a larger project, lots of types, and maybe just like it's getting typescripts, getting slow for them. Like the type checking process. Is there anything they can do to maybe help speed it up just kinda as an easy first step. Yeah. The easy first step is using the incremental flag. You can write you [00:21:00] can run TypeScript compiler with minus, minus incremental, and with that, TypeScript will save the compilation information in a file. It's by default t as built info, unless you specify other ways in your TypeScript compiler configuration. And then, Whenever you have to wear around the TypeScript compiler, it'll check the information is saved in that file and it'll calculate like the, what's the minimal required work it has to do to compile your code? Because, for example, maybe you only change one file, so why compile the whole code base? It can only recompile like this particular file or like potentially some dependencies that are needed. Gotcha. Are do you know if not, it's okay, but do any editors, I'm thinking just does VS code, for example, does it use that incremental flag under the hood? Will it put that on your project for you just at normal dev time to make its internal system faster? Yeah, like in the language [00:22:00] server. I don't know for sure, but I suppose it, it does. Yeah. It would make a lot of sense. Yeah. Yeah. Gotcha. Yeah, I would figure it would as well, but I could, there's probably some justifications not to, I just wasn't Yeah. To be honest, I never I never checked it out, but yeah, that's a good question. But I'm pretty sure it does. Yeah. Yeah. Yeah, I would be surprised if not as well. I'm sure. I'm sure we could figure it out. Cool. Yeah, if there's anybody kinda, if, I guess if people are encountering issues with TypeScript or they just want to get involved in the development processing community, what's the easiest way for them to do so if you had noticed. An issue that you is slowing your project down and you went through the, like all the steps used incremental. You you generated the trace and you notice something unusual and you don't know how to proceed. You can open a new issue in the TypeScript repository, but before opening an issue, there are a few things that you have to do. For example you have to look for. Potential duplicate. Maybe someone already had this problem and they already opened an issue.[00:23:00] You also have to make sure that you're using the latest TypeScript because maybe this issue was already fixed in the latest version. So that's it's worth checking that out because if you create an issue And the problem was already solved. It can it can save some time for the maintainers to check it out yourself. And once you know that there are no duplicates and you made sure that you're using the latest types screw version you can open the issue and include as many information as possible. Firstly, like the minimal reproduction. Like how to reproduce the issue that you are having. Maybe your project is open source so you can also leave the link to, to, to the repository. It's also very useful to include the the trace that you have that you can get by running the generate trace flag. The output from extended diagnostics, like all of that. When you, when like many nurse have all of that [00:24:00] in the issue and they can, maybe, they can just take a look and, they, they will know right away what's going on and they can like they can provide you with some information how to proceed, or maybe someone else from the community had a similar issue and they can also help you. And otherwise if it's more complex, like I'm sure someone will debug it further from the TypeScript team and the issue will be solved. Nice. Awesome. Yeah, are there any closing thoughts or anything you'd point listeners to in the TypeScript space? Implore them to check out. I think my, my, my final thought is that you have to keep TypeScript up to date because it's it's working really nice. And also don't be afraid of using those generate trace flags or extended diagnostics like, Even if it seems intimidating at first it did seem to me like very intimidating when I first run it. I was like, I opened the [00:25:00] trace and I had no idea what was going on and what to do with that. It's like you can slowly, poke around, play with it see different things, how they behave maybe do some changes to your code base and see how the trace changes or how the extended diagnostics output changes and Yeah, I think that's a good exercise to do. You can learn a lot about your projects and if you can also like, optimize something, if you can make the type clip compiler faster and your editor support faster, thanks to that, it's another win for you. Gotcha. Cool. Thank you so much for coming on and chatting with me, Alexandra. It's been a pleasure. Thank you a lot for having me. Of course.