Announcing TypeScript 7.0
84 points by evert
84 points by evert
I get why, but it’s interesting that they moved from a self-hosted compiler to a not self-hosted compiler. That’s a direction you don’t see much of, it’s almost always the other way around.
I think it's something we should see more of. Not every language is great for compilers. Self-hosting puts strong evolutionary pressure on the language to become a language that's better for writing compilers, which might not be the right direction.
Tooling in general is the same. I get that writing tools for language X in language X makes it easier to get contributors in the community, and that's a very real benefit. But, as an example, look at the performance improvements Astral has made by (re)writing tools in another language (Rust, in this case).
It can backfire. Some many years ago in PHP development we saw some tendencies where the people who were developing the language were migrating from being web developers to purely being C programmers and it felt like getting out of touch a bit. The people who came from being users of a language solving problems for a (scoped) problem didn't work in the scope anymore.
This was around the time when the ecosystem shifted more towards "we don't need it in the language" but before composer and nicely installable user space libraries (PEAR wasn't so great a this).
I think it's less of a problem for "general purpose" languages and I guess nowadays people use TS for backend just as much, and it's 20 years later and people do write more general purpose languages.
yeah, Gleam does this and it feels like a definite strength. rust is a much better language for implementing a fast compiler, LSP, etc etc
Also, Gleam supports multiple first-class compile targets as a feature, none of which currently are native, which seems to me at odds with self-hosting. Would the self-hosted compiler run on BEAM or JS (and then within JS, Node, Deno, ?) What if that's not the platform you're interested in targeting for your project? Are there multiple implementations of the compiler with bindings/shims for each platform? Yuck.
Keeping it in Rust sidesteps that entirely.
It also means you don't accidentally lose your language's bootstrap path or make it take a horrifically long time to bootstrap.
Yeah absolutely. Rust is only buildable with the version before the previous release IIRC, so version 1.60 must be built by 1.58 which must be built by 1.56 etc, all the back to the OCaml compiler. So every other release, the bootstrap path includes yet another compiler build.
That's how compilers become practically un-bootstrappable.
Not all the way back, there are Rust compilers written in C. They're not technically complete but they're able to build rustc which you can then use to build rustc. Of course that just moves the problem on to bootstrapping a C compiler...
FWIW bootstrapping a C compiler is on the critical path to pretty much everything and is a well understood (albeit complicated) problem.
I think that it's important that a language is dogfooded in some way. A compiler is the easiest way to do it. If a language isn't meant to be good at making a regex engine, but is meant to be really good at X, you kind of hope the author has a giant X project concurrently developed, so they know whether it's good or bad. Jai is obviously likely to succeed at being good for making video games, because Jonathan Blow is making video games with it and it exists primarily for his personal use. But most languages are made by people for whom languages are their primary development interest. Only giant corporations like Microsoft can get an easy pass here, since they're doing a little bit of everything and so there's probably someone internally properly dogfooding the functionality even if its primary developers aren't (ex: the VS Code team).
Language designers and implementors understanding that their language isn't the perfect language for every use-case? Incredible.
True, but JavaScript runtime performance is just intrinsically slow. Unless you need to write something in JavaScript, if you care about performance eventually, you'll want to port to a faster language.
The single-threaded performance of JS isn't bad in a well-designed project (which TypeScript was); the main win here is from multithreading, which is (currently) annoying and expensive in JS.
(Edit: actually, apparently even single threaded it's still 3x faster, which is more than I'd have expected!)
Single-threaded JS isn't that slow (a few times slower than C/Rust/Zig/etc as you say), but it takes a while to get fast. TSC doesn't get fast until V8 has JIT-compiled it. So you're not just compiling your code, you're also compiling your compiler every time.
True but the 10x claim was about very long jobs - V8 has surely finished JIT'ing tsc after a couple seconds into the 60 second it takes the old codebase to check VSCode.
Another thing that makes tsgo faster is better memory layout: more compact objects, and less need for pointers between objects. That’s one of the reasons they chose Golang rather than C#.
Small tip from personal experience. The project I'm currently assigned to uses TS 5, and (as expected) I don't want to use that in nvim. So first I installed TS 7 with mise, then inside node_modules/.bin I created a symlink called tsgo pointing to the TS installed by mise. And since nvim-lspconfig (still) uses the old tsgo filename (from it was still a technical preview), nvim runs tsgo .i.e. TS 7 installed of the local TS.
If you're not coding in TypeScript, will these improvements affect the speed of vscode itself (since it is coded in TypeScript), or only the TypeScript language extensions?
Typescript compiles to JavaScript. When VSCode is running, node.js and Chromium is executing JavaScript code. The only thing that's getting faster here is the compiler which takes Typescript code and turns it into JavaScript -- which happens in Microsoft's build pipeline before they ship the VSCode executable to you.
The only potential exception is that maybe the Typescript LSP server will get faster too, since that actually does parse your Typescript code at runtime. But this is unrelated to VSCode being written in Typescript.
I know this comment is more or less just repeating stuff others said but I felt like some detail was missing.
It's worth noting that the way they made the choice it ends up being unrelated, but the choice itself was (should have been) deeply related.
E.g. VSCode used to keep all its core editor state in C++, but then they realized it was a big perf problem to constantly be calling back and forth between C++ and Javascript. Each system call messes up cache locality and wrecks a whole raft of otherwise-possible code optimizations. They were needing to go back and forth between C++ and JS so often that most of the time was being spent in the FFI bridge.
The solution for VSCode was to move the core state into Javascript, so that Javascript plugins would be using plain JS APIs. The result was "blazingly" fast and VSCode has been on top ever since.
This same dynamic could have driven TS to become become the primary API platform for coding, but they summarily gave up on that, leaving the actual most important work in the project's history -- the ground-up re-architecture of their JS APIs to create an API platform -- to be done by me.
Typescript's compiler reallllllly doesn't do much (there's a bunch of stuff that gets marked as WONTFIX because of the principle that tsc shouldn't really actually mess with the code it's processing beyond the bare minimum of, like, stripping types). So I would expect basically no change in perf characteristics of tools built on typescript
It will only speed up programs that are executed by TypeScript’s type checker, for example Doom.
I'm shocked by the memory numbers. JS vs Go is within 10%. Wat?
On the serious side, I'm guessing, it's just the beginning as TS 7 is a port, not a re-write. Likely most of the data structures are just maps all over the place for now. But I'd love to learn more from a real TS hacker!
I'm shocked by the memory numbers. JS vs Go is within 10%. Wat?
I guess that’s related to the TypeScript checker threads repeating work instead of using shared mutable data, so although tsgo has smaller objects, it has more of them.