A 10x Faster TypeScript

156 points by whjms


andrewchou

The TypeScript dev lead posted this response about the language choice on Reddit, for anyone who’s curious:

(dev lead of TypeScript here, hi!)

We definitely knew when choosing Go that there were going to be people questioning why we didn’t choose Rust. It’s a good question because Rust is an excellent language, and barring other constraints, is a strong first choice when writing new native code.

Portability (i.e. the ability to make a new codebase that is algorithmically similar to the current one) was always a key constraint here as we thought about how to do this. We tried tons of approaches to get to a representation that would have made that port approach tractable in Rust, but all of them either had unacceptable trade-offs (perf, ergonomics, etc.) or devolved in to “write your own GC”-style strategies. Some of them came close, but often required dropping into lots of unsafe code, and there just didn’t seem to be many combinations of primitives in Rust that allow for an ergonomic port of JavaScript code (which is pretty unsurprising when phrased that way - most languages don’t prioritize making it easy to port from JavaScript/TypeScript!).

In the end we had two options - do a complete from-scrach rewrite in Rust, which could take years and yield an incompatible version of TypeScript that no one could actually use, or just do a port in Go and get something usable in a year or so and have something that’s extremely compatible in terms of semantics and extremely competitive in terms of performance.

And it’s not even super clear what the upside of doing that would be (apart from not having to deal with so many “Why didn’t you choose Rust?” questions). We still want a highly-separated API surface to keep our implementation options open, so Go’s interop shortcomings aren’t particularly relevant. Go has excellent code generation and excellent data representation, just like Rust. Go has excellent concurrency primitives, just like Rust. Single-core performance is within the margin of error. And while there might be a few performance wins to be had by using unsafe code in Go, we have gotten excellent performance and memory usage without using any unsafe primitives.

In our opinion, Rust succeeds wildly at its design goals, but “is straightforward to port to Rust from this particular JavaScript codebase” is very rationally not one of its design goals. It’s not one of Go’s either, but in our case given the way we’ve written the code so far, it does turn out to be pretty good at it.

Source: https://www.reddit.com/r/typescript/comments/1j8s467/comment/mh7ni9g

strugee

Does this mean that the first frame of Doom can now be rendered in only ~1.2 days?

msfjarvis

Another one in the long list of JavaScript tools that ditched JavaScript as their implementation language for performance reasons. Hopefully this is more easily usable by the time I have to work on a NodeJS project again, because the performance improvement numbers look incredibly promising.

shanemhansen

Echoing and consolidating what others have said.

The c# author chose go for this project rather than c#. It’s not because go is “better” but it’s better for tsc. He said that c# is fundamentally a bytecode first language and he doesn’t consider it’s aot capabilities to be that mature. So essentially he thought binaries built by go would be a better deployment target due to better (in his opinion not mine) cross compilation support. He also mentioned that he considers low level memory/struct layout to be easier to do in go and presumably he wanted that in this project.

In a way go won because it was a syntactically similar natively compiled garbage collected language with the best cross compilation story.

Rust seems to have been ruled out because it either would have required reworking the object graph to comply with rust ownership rules (believed to be harder than line by line transliteration) or they would have had to GC<Box<T>> all the things. I presume they ruled that out because it was ugly and painful to work with but I don’t know if that’s true.

Despite how good javascript jits can be, they don’t shine at the polymorphic graph analysis compilers do. If everything was being manipulated as a big byte array, v8 would probably have been more competitive.

So the tl;Dr is that ultimately golang was closest 1:1 transpile target that met their needs.

janiczek

Maybe this will bring the idea of “TypeScript tooling should run the typechecker before doing anything else” (like linting, running tests, etc.). It feels very backwards that you can make a bundle with vite or whatever without the code being in a compilable state.

osa1

I love the comments from their GitHub discussion on “Why Go?”.

One of the top language engineers in the world makes a decision on which language to use.

Randos on GitHub:

peter-leonov

So, in fact, Go is a sort semantically faster JS (given the duckily typed interfaces and reflection) if compared to C/Rust on a large rather conservative (no evals, method_missing and friends) codebase.

minimax

Hang on… by “native” they mean “in Go”? Interesting choice!

janus

Is this the first big Go project at Microsoft? I had no idea they could choose Go, I would assume that it just wouldn’t be acceptable, because they wouldn’t want to unnecessarily legitimize a Google project.

christoph

if its not possible to rewrite it in typescript in a way that is as fast as the go version, that really sheds a bad light on v8 and its performance and begs the question why you should write any non browser code in typescript.