DEV Community

Aleksei Berezkin
Aleksei Berezkin

Posted on • Updated on

TypeScript is slow. What can we do about it?

Edit on 11 Jan 2022

Since the moment of writing this post a lot of things changed in Language Server, and performance is much better now. Some issues with TS are still actual, so a leave the post as is.


Before the takeoff

I was working on my project when noticed my laptop fan is spinning like before the takeoff. I checked htop and found out there's a node process spawned by WebStorm, and this process' CPU consumption skyrockets to 100% each time I edit a TS file. It took 10–20s for the process to finish its work and to release the CPU.

I started googling and encountered quite a few issues about TypeScript and crazy fan spinning submitted both to TypeScript and WebStorm. Unfortunately they where barely helpful so I decided to make a small research.

Profiling the language service

I asked for a suggestion on JetBrains community forum and was kindly instructed to take V8 profile. It showed me that there is some heavy 20-seconds computation triggered by getSemanticDiagnostics:

FlameGraph

getSemanticDiagnostics is a TS language service method which analyzes a file for errors like “x is not assignable to type y”, “type x does not have property y” etc. It seems okay that WebStorm invokes it on each edit, but what is it doing that long? Is there a busy waiting or an endless loop? To understand it I decided to get my hands really dirty.

Debugging the language service

I attached to the node process and paused it several times. There was very very long stack:

Debugger stack

On the first sight it seems like it's too hard to find out what's going on; but actually there are things revealing general picture.

First, there's a loop iterating over all statements in the file:

forEach

Next, down the stack, there's a type inference request for a specific place in my file which is visible through arg:

Infer types

Apparently this request is executed for every single part of the file. Next, it lands onto the long recursive chain of checkTypeRelatedTo(), isRelatedTo(), recursiveTypeRelatedTo() etc. which, as seen, performs the real CPU-intensive work of inferring types.

isRelatedTo calls

But why is type inference that heavy?

Indeed, a lot of languages can infer types, is there something special about TS? Well, I see two things:

  • TS type system is exceptionally rich and powerful, way more powerful than that of Java or Scala. This also means that the size of a code that infers types is huge.
  • Unlike many other languages, TS is written on a slow scripting language — JavaScript. Now, don't get me wrong, I love JS and appreciate its value for web, but... It wasn't intended to build so complex and computational-demanding things like TypeScript! Which I also love 😢

So the problem is clear now

  1. On each edit, WebStorm calls TS Server's getSemanticsDiagnostics method
  2. The method analyzes the whole edited file, running types inference where needed
  3. That type inference is very, very expensive, and furthermore seems it's not linear of the file size

Or, putting it in one short conclusion:

TypeScript is slow by nature. Its performance degrades with the file size, and the relation is likely non-linear.

But what can we do?

Just keep files small. How exactly small depends on your computer, and what fan noise you can tolerate 😀 I personally try to stick to these limitations:

  • No more than ~400 lines
  • No more than ~20 imports

Perspectives

TS codebase grows with each release bringing us new cool features. But this also means one day even short files will burn out our CPUs! What can the TS team do about it? I'm not a sci-fi writer to predict the future, but I'll try 🤓

So, TS team can:

  1. Migrate TS to some compiled language. Too late perhaps, but who knows 😉
  2. Make TS language service able to partially analyze a file. This requires very deep integration between the service and an editor, which may possibly bring other hard problems.
  3. Utilize WebAssembly. It's not stable in node for the moment, but this will happen one day. The language service may be split to API and computational parts, and latter may be compiled to WASM.
  4. Develop or adopt a tool compiling JS (TS) to some lower-level representation like LLVM or even to the native code!

I believe TS team adopts (3) or (4). Furthermore, I suppose it's partially possible even now! However, that's not the way for the webdev majority, and the TS team needs to implement some tooling to simplify the process. Yet, of course, editors need to support an optimization path which TS team chooses.

Staying tuned

Whatever the technology is, it has limitations, and TypeScript is no exception. And we must admit, there's certainly a room for improvements! So, staying tuned for news!

Thanks for finishing this reading. If you find it interesting, please consider leaving some feedback or following me on DEV.to or Twitter. I'm new here and I'd be glad to know if this kind of stuff is helpful. Thanks.

Discussion (111)

Collapse
futureistaken profile image
R Z • Edited on

Webstorm itself is very slow.
I never saw any problems with TS using VScode. Everything works very very fast, including TS.

Before giving any recommendations about what TS team must do, throw away your laptop out of the window and buy a good PC. The second option, get rid of the webstorm.

I did the exact things that have been described above and I am happy.

Don't forget that people need high-performance PCs not only for gaming, but also for working with modern technologies.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

Thanks for your suggestions! But I'd better keep that MacBook Pro with me 😀 In VS Code the effect is less noticeable, yes, but only to some degree. With enough memory tsserver consumes much more CPU than the editor. Yet, tsserver performance is the same regardless of the editor.

Collapse
ksnyde profile image
Ken Snyder

If you do a lot of inference it doesn't matter what beast of a machine you have. I use vscode on a iMac Pro with 14 processors and I am dealing with types that do not resolve when you hover over them for 15-60 seconds. It's intolerable. I guess I got a bit carried away with strong types and now I need to reverse course but I wish I could get a better feel for where the real pain points are coming out.

Thread Thread
alekseiberezkin profile image
Aleksei Berezkin Author

Exactly. What is inherently slow cannot be fixed just by taking another computer, that was my point.

Collapse
futureistaken profile image
R Z

On my PC TS works very fast in any environment.

Thread Thread
alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

Sure, having a big and powerful computer is better than having a small and limited laptop. Personally I don't like laptop keyboards (and especially Apple touchbar), I don't like small screens, I don't like fan noise, I don't like carrying it with me. But unfortunately I have to. Using laptop is always a compromise. It's not the best equipment I can imagine, it's just the most adequate for me.

Collapse
dbanisimov profile image
Denis Anisimov

Sure, TS is not blazing fast and you've pointed out the main reasons for that, mainly having a very powerful type system. It worth noting that TS analyzes not only the source files in the project, but also all of the dependencies, so the source size doesn't matter as much as the overall tree size. Most of the huge performance problems I've seen were due to some 3rd-party library trying to create very complex types, which library maintainers and TS maintainers usually take very seriously and eventually fix or improve type inference performance. It's always a good idea to keep TS version up-to-date as the team ships perf improvements in every release. (dangerous tip: you can boost TS performance by using an option --skipLibCheck)

I'm personally a bit skeptical about options 3 and 4. Native code is not a silver bullet, and a scripted interpreted language doesn't imply bad performance. IMO the way forward is for node to implement more advanced JIT optimizations and for TS to focus on higher-level optimizations, like caching and improving the IR and algorithms.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

Thanks for an insightful comment. That's true that not only file size matters; I've noticed that the number of imports matters. This is similar to what you write about the overall tree size.

I agree that optimizations I speculate may not be the focus of the TS team; that's why I wrote it's a sci-fi 🙂 However, V8 optimizations are also not a silver bullet; V8 is already heavily optimized.

About TS optimizations — yes, agreed. There are some issues in the TS repo like “let's memoize something”, and the code I saw looks like there is not that much cached. But, you know it, caching is hard: keeping editor and TS Server in sync is very hard problem.

Anyway, looking forward for news, I believe something interesting happens soon!

ivanjeremic profile image
Ivan Jeremic • Edited on

Yeah we know it but the reality is if you want to start using C++ for everything good luck. Also I have no idea why the negativity against wasm you know that the internet (browser) is a place where where amazing software runs not everything can be in app stores or local software. Being in the browser gives you the advantage that you can sit on any device and start working where you left on the other device, SaaS software like monday.com, hootsuite, Figma, or things like CodeSanbox, imagine them being only locally available they would be useless. What I want to say the future is cloud for the most part and the user facing Apps and UI will not be 100 % nativ but the speed will be almost there so end users will never be able to tell.

Collapse
joelbonetr profile image
JoelBonetR

Can you, please share your laptop specs for adding context?
I developed with TS using a shitty i5 8250u and 16Gb RAM, also tried with a R5 3500u with 8Gb RAM and it was fine, next week I'll try with my new office laptop which is an i7 1065G7 with 16Gb RAM and I'll see if there's any visible improvement

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

It's good but usual MacBook Pro with i7 and 16 GB RAM. Most of the time it's fine; interesting things begin when the file size approaches 1,000 lines and heavily uses type inference. Funny is that not specifying types and letting a compiler figure them out is somethings we like TS for 😀

Collapse
tominekan profile image
Tomi Adenekan

i5 is sh**ty? Try working with Intel celeron and 4 gigs RAM. 😂

ivanjeremic profile image
Ivan Jeremic • Edited on

I love to write apps with the most used and most flexible language in the world:)

alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

Could you please elaborate a bit on this? Because I actually heard the opposite.

The Effective TypeScript by Dan Vanderkam discusses this in details:

Chapter 3. Type Inference

Item 19. Avoid Cluttering Your Code With Inferrable Types

(...) in TypeScript many annotations are unnecessary. Declaring types for all your variables is counterproductive and is considered poor style.
(...)
Ideal TypeScript code includes type annotations for function/method signatures but not for the local variables created in their bodies.

Things to remember

  • Avoid writing type annotations when TypeScript can infer the same type (...)

I agree that this may be the place where the beautiful theory meets the harsh reality. But anyway, type inference is something every language is sort of proud of, and one of their promotion points. And keeping away from this feature looks like artificially limiting your expressiveness.

Collapse
shaunakde profile image
Shaunak De • Edited on

My thoughts precisely. For more time then care to admit I spent working on close to a million line C++ codebase in Visual Studio. Compile times were coffee break times, but I never felt IntelliSense lagged in any way. I work in golang now. While compile times have dramatically gone down (I also work on a much smaller codebase now) - the tooling never seems to live up to my memory of Visual Studio 6. The cold hard truth of computers is that interpreted languages will (as of 2020) be slower then compiled ones - even for clever developer tooling.

Having said all this - I never want to write a website using TreeFrog ever again.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

Is Golang interpreted? I haven't yet tried, but Wikipedia suggests it's compiled...

Thread Thread
shaunakde profile image
Shaunak De

No, it's compiled. But its biggest party trick is that it compiles really quickly. A small example: stackoverflow.com/questions/297663...

Thanks for writing this blog post. I really enjoyed learning about Typescript. Front end development is opaque to me, and I like atleast knowing what's new!

Collapse
elmuerte profile image
Michiel Hendriks

Isn't the real problem TS's language server? Why does it need to visit so many files every time. Can't it just keep around the "compiled" results? It doesn't need to rebuild the whole AST for the file you are editing... right? Or is this a limitation of the language server protocol design?

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

These are very good questions! The server could have cached something but this, I suppose, would only trade problems: if there's caching, chances are the editor and the server may get out of sync. Invalidating caches is always a hard problem.

Collapse
elmuerte profile image
Michiel Hendriks

Sure, but the IDE should obviously communicate changed regions. This how IDEs work internally with languages they support as first class citizens.

Thread Thread
alekseiberezkin profile image
Aleksei Berezkin Author

I'm not sure TS supports this. Do you have some insights?

Collapse
jwp profile image
John Peters • Edited on

My thoughts exactly. The language service takes time and was recently rewritten.

Collapse
prinzsabishi profile image
Michał Pędzik

Interesting article! I would love to see TS being written on some compiled language.

Collapse
joelbonetr profile image
JoelBonetR

Doesn't Web Assembly are meant to be the performant front-end executable?
I mean, you can write on Rust, Kotlin and many others and compile it into web assembly if you are going to the better performance.

Collapse
prinzsabishi profile image
Michał Pędzik

Probably, you are right with this. I am beginner so I don't know which is good and which is bad in the topic of performance. I am just curious! That's it :)

Thread Thread
joelbonetr profile image
JoelBonetR

Haha no problem, there are many languages and tools around, it's a community issue to use the fancy tool instead of the correct one for the job.
When you're about to release let's say a landing page you must not use any JavaScript Framework such react, angular, preact, svelte... with or without TS which is only a JS superset. Nor will you use a java or C# environment because it's a waste of resources, you need to choose plain HTML, CSS (sass or scss on dev time) and the minimum JavaScript possible due to performance and specific use case / target (which on a Landing page it's SEO and speed).
By the other hand you can use angular, preact, react, svelte... On a private web app to provide a service to your customers (having or not a node back end, this is not important here) due to user experience and easy to develop and scalability for example. There are many use cases and few better options to reach the desired target easier or on a better way regarding to the future 😁

Collapse
ivanjeremic profile image
Ivan Jeremic

I would claim that it is useless and unproductive more time is spent on finding types and fixing types than developing, good eslint setting and good editor are better that TS in my opinion, TypeScript and Types developed a hype similar to Apple products almost a Type fanbase who type everything everywhere and all he thinks is types types types, if you are one of there who use TypeScript to detect missing imports or underline unused variables and functions then you don't need TypeScript, this is also possible without it.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

I hear this quite often about TS. However, IMO there's no controversy: if you feel you don't need types, you actually don't need them. Lucky you! Your editor is fast, and npm scripts are short. You are happy developer who've never worked in large old enterprises in its worst sense. Having types in such projects is usually the only good thing about them.

Collapse
ivanjeremic profile image
Ivan Jeremic • Edited on

I'm not against types but it gets crazy if types are more code then my actual code, I'm for to only type what is really needed like what gets returned that's it.

Thread Thread
manitchanthavong profile image
Manit-Chanthavong

TypeScript pulled me back into Node.JS development. . Without TypeScript I would have ditched node and moved onto a different platform altogether.

IMO, developing with node, typescript, vscode is a whole lot faster and more enjoyable than asp.net core, c#, and vs. However, for large Node apps, there are some serious development patterns you have to be aware of to make your app successful.

Thread Thread
0916dhkim profile image
Danny Kim

Same here. I would never have come back to nodejs if there wasn't TS.

Collapse
chriskruining profile image
Chris Kruining

we're 2 years further along and this post seems just as relevent to me now. Any updates or tips how I should improve my compile times? Personally I have a concept of utilizing deno's rust port of the compiler, but I have about 0 clues on how to get started on that rabbit hole.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

No, haven't tried yet unfortunately. To improve compilation times I try to keep files small, and avoid heavy type inference.

Collapse
ecyrbe profile image
ecyrbe

You can use the typescript compiler deno has started to use that is written in Rust:
github.com/swc-project/swc

Build typescript faster. Now you need to replace ts server with swc but that's another project that is not created yet.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

Interesting! How quickly does it pick up new features of TypeScript? There's not much in the docs.

Collapse
ecyrbe profile image
ecyrbe

Yes, their documentation doesn't directly point to features, but in fact they are listed in comparison with Babel here:
swc.rs/docs/comparison-babel

Collapse
stereobooster profile image
stereobooster

I disagree with some points in article, but... big kudos to author who responses with grace to all bad takes in comments

Collapse
ender_minyard profile image
ender minyard

JavaScript has performance limitations. We can see this with slow SPAs. Now, with TypeScript, we're seeing the limits of JS as well.

If I were using TS, I would always target WASM, then do a plain JS fallback for browsers that don't support WASM (module/no-module pattern).

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

Yep, WASM is promising, and its support in node is coming. I'm talking about node because I'm discussing the development process here which is usually supported by TS server running on node. But anyway, a lot of applications, as expected, may benefit from full WASM support.

Collapse
retronav profile image
Pranav Karawale

I see some options for this TS issue(fingers crossed for this to be finished):

  • Wait for swc to implement type checking
  • Compile tsc using nectarjs (I tried but got lot of errors)
  • Bear with ye good ol' tsc
alekseiberezkin profile image
Aleksei Berezkin Author • Edited on

any works the opposite way 😀 any tells TS and IDE to stop inferring or checking a type of a var. Thus, if your code is full of anys, compilation will be the fastest. In my example there's not a single any, all types are strict.

alekseiberezkin profile image
Aleksei Berezkin Author

Knowing types from a context is not a type inference? Whut? 😲 Then what is type inference, if not this? How does editor “know” that i in your C# example is a string? Compiler (or editor plugin, the same) infers its type for you. To achieve this, it constantly monitors all changes (just like TS server), and restarts the process whenever something is changed (just like TS server). Otherwise how this would work do you think? All languages work the same. For TS there are certain things which make it slow, and this can be improved — that was my point.

ivanjeremic profile image
Ivan Jeremic

For me the browser of today feels better than 5 years ago and it keeps improving since I know that browsers exist, What you want is a perfect world how we ship apps and in what language they are written but other then streaming the apps to the user so people use apps on play button like a Netflix video or like a Google Stadia game I can't think of an other solution this way whole operating systems could be streamed to the users and everyone walks around with devices able to open this, and again most likely it will be browser on this devices that are used for this as you can see with Google Stadia so I don't know what the future is but it is for sure not only Native/Local apps

alekseiberezkin profile image
Aleksei Berezkin Author

I think that may be a matter of measure. And taste to some extent. Best practices have some exceptions; or these are the part of a best practice. However, I doubt you like an example without a single inferred type.

// 3 types inferred: upper, i, output of map
const upper = (a: string[]) => a.map(i => i.toUpperCase())

// 3 types inferred: a, i, map output
// This is recommended in Effective Typescript
const upper: (a: string[]) => string[] =
    a => a.map(i => i.toUpperCase())

// All types are explicit
const upper: (a: string[]) => string[] =
    (a: string[]) => a.map<string>((i: string) => i.toUpperCase())
Collapse
zyzmoz profile image
Daniel Cunha (he/him)

I'd say that your post title should be "Programming Best Practices"...
Webstorm and typescript might overload your computer resources for a number of reasons. If you take account of what your IDE/Editor, bundler, compiler, lint, and others do, you'll see that larger files are way expensive to perform actions (compile, syntax checks, etc) than smaller ones...
Still, you shared valuable content! Thanks :)

PS.: I've been using a MacBook Pro 2013(i5/ 8gb RAM/ SSD) to work with numerous projects of different complexities and sizes. Therefore, it works fine with 4 VSCode instances, servers, RMDBS, Browsers, Spotify and more

Collapse
himujjal profile image
Himujjal Upadhyaya

I therefore started ekscript.com
Because I thought Typescript was now mature enough to have a standalone compiler/interpreter.

Your post is absolutely correct. I therefore had to ditch VS Code or JetStorm IDEs in favour of Nvim-QT. I don't face the problems you do though. Everything is snappy.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

Thanks for sharing a link! Is this the repo? github.com/ekscript/ekscript There's no code 😕

Collapse
himujjal profile image
Himujjal Upadhyaya

It is. There's no code. Just the concept put out. I started it. I hope i pull this off in future though. not giving any hopes though. just that typescript is an awesome language

Collapse
ivanjeremic profile image
Ivan Jeremic

Node is amazing.

Collapse
jwp profile image
John Peters • Edited on

Here it is 2 years later. We know tons of work were put into the Language service. Always a tacit admission of a problematic area. Are things better now?

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

Yep, it's much faster now. Happily, I own the same laptop and still maintain projects which I've been working that times. On the moment of writing the post TS used to run for 10–15 seconds to infer types on some heavy files. Now it runs for 2–3 seconds. Thanks for your comment, I'll put a notice at the top.

Collapse
jwp profile image
John Peters

Just wanted to commend you on the excellent analysis you did 👍

Collapse
matthewpardini profile image
Matthew Pardini

I think webstorm is your problem here. What version of typescript were you on?

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

The latest stable version (4.0.3). As I already answered somewhere, with VS Code the effect is less visible. But tsserver was consuming exactly the same CPU (100% for ~15 seconds) in both cases.

Collapse
matthewpardini profile image
Matthew Pardini

Well if you read the GitHub issues on this, it does appear that there have been a few concurrent issues since sometime in version 3.3.x. VsCode handles it better by clearing cache automatically for you. Sure typescript could be faster, but why not use a better suited IDE for the job?

Thread Thread
alekseiberezkin profile image
Aleksei Berezkin Author

Again, tsserver process performed literally exactly the same for both editors. WS heaviness is the different topic, perhaps a subject for another post 😄 That's why I'm not going into these details here.

Thread Thread
matthewpardini profile image
Matthew Pardini

There are GitHub issue comments from 2019 saying that in vscode tsserver does not do that if you follow certain steps. And in a later version of vscode made those steps automated. If you’re saying it is still doing that on the latest version, you ought to file a new bug. The ide isn’t irrelevant.

Thread Thread
alekseiberezkin profile image
Aleksei Berezkin Author

You are right. I found an open issue which was lacking an example file and attached my file on which issues reproduces. Thanks for convincing me. github.com/microsoft/TypeScript/is...

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

Not only tools for TS are written on TS, the TS itself is written on TS! What you described, I'd call it a “prototype trap”. It's so fast and nice to quickly hack MVP on JS or Python, but it's very painful afterwards if the prototype evolves to the product as is. However writing everything on C++ from the very beginning may increase the initial cost of a product. Sooo... It's hard, and there's no simple answer 😢

Collapse
jwp profile image
John Peters

This comment makes no sense to me at all.

Collapse
louislow profile image
Louis Low • Edited on

WebStorm is a resource eater. The IDE also makes my expensive DELL workstation PC noisy after few hours of use. I had to use WebStorm because I still have few weeks left before the license expired. And it is expensive too.

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

WS is definitely CPU-demanding, however, it's the price to pay for its cleverness. But anyway, I'm discussing here only tsserver performance which was the same regardless of an editor.

ivanjeremic profile image
Ivan Jeremic • Edited on

We all know these statistics, and listening to them it sounds like the web is dead and there are no jobs and nobody uses it, which is totally not true and it is the opposite, I think there will always be both.

Collapse
bennypowers profile image
Benny Powers 🇮🇱🇨🇦

Use esbuild?

Collapse
alekseiberezkin profile image
Aleksei Berezkin Author

Is it possible to integrate WS or VSCode with this tool? Briefly looked into a documentation, haven't found anything about it.

Collapse
bennypowers profile image
Benny Powers 🇮🇱🇨🇦

esbuild won't do any typechecking, just transform to javascript

I prefer to do it with @web/dev-server for a buildless development workflow

modern-web.dev/docs/dev-server/ove...

Collapse
cyberhck profile image
Nishchal Gautam • Edited on

Try a i7 ssd computer with Linux on it and see if you notice any difference at all (you should).

ivanjeremic profile image
Ivan Jeremic • Edited on

C# is in my opinion also a scripting language, and I already told you that this is a myth there a tons of SaaS products running in the browser successful and millions of people use them, I know loosing clients to web devs must hurt but instead of fighting about languages just do your thing and write software how you think it is best. Also my focus is on desktop I don't think you will ever walk into an office and see people have no PCs and only work on smartphones.