Skip to content(if available)orjump to list(if available)

A 10x Faster TypeScript

A 10x Faster TypeScript

678 comments

·March 11, 2025

DanRosenwasser

Hi folks, Daniel Rosenwasser from the TypeScript team here. We're obviously very excited to announce this! RyanCavanaugh (our dev lead) and I are around to answer any quick questions you might have. You can also tune in to the Discord AMA mentioned in the blog this upcoming Thursday.

spankalee

Hey Daniel.

I write a lot of tools that depend on the TypeScript compiler API, and they run in a lot of a lot of JS environments including Node and the browser. The current CJS codebase is even a little tricky to load into standard JS module supporting environments like browsers, so I've been _really_ looking forward to what Jake and others have said will be an upcoming standard modules based version.

Is that still happening, and how will the native compiler be distributed for us tools authors? I presume WASM? Will the compiler API be compatible? Transforms, the AST, LanguageService, Program, SourceFile, Checker, etc.?

I'm quite concerned that the migration path for tools could be extremely difficult.

[edit] To add to this as I think about it: I maintain libraries that build on top of the TS API, and are then in turn used by other libraries that still access the TS APIs. Things like framework static analysis, then used by various linters, compilers, etc. Some linters are integrated with eslint via typescript-eslint. So the dependency chain is somewhat deep and wide.

Is the path forward going to be that just the TS compiler has a JS interop layer and the rest stays the same, or are all TS ecosystem tools going to have to port to Go to run well?

lytedev

Reading the article, it looks like they are writing go, so will probably be distributing go binaries.

maxloh

Maybe they'll also be distributed in WASM too, which is easier to be integrated with JavaScript codebases.

no_wizard

Like others I'm curious about the choice of technology here. I see you went with Go, which is great! I know Go is fast! But its also a more 'primitive' language (for lack of a better way of putting it) with no frills.

Why not something like Rust? Most of the JS ecosystem that is moving toward faster tools seem to be going straight to Rust (Rolldown, rspack (the webpack successor) SWC, OXC, Lightning CSS / Parcel etc) and one of the reasons given is it has really great language constructs for parsers and traversing ASTs (I think largely due to the existence of `match` but i'm not entirely sure)

Was any thought given to this? And if so what was the deciding factors for Go vs something like Rust or another language entirely?

DanRosenwasser

We did anticipate this question, and we have actually written up an FAQ entry on our GitHub Discussions. I'll post the response below. https://github.com/microsoft/typescript-go/discussions/411.

____

Language choice is always a hot topic! We extensively evaluated many language options, both recently and in prior investigations. We also considered hybrid approaches where certain components could be written in a native language, while keeping core typechecking algorithms in JavaScript. We wrote multiple prototypes experimenting with different data representations in different languages, and did deep investigations into the approaches used by existing native TypeScript parsers like swc, oxc, and esbuild. To be clear, many languages would be suitable in a ground-up rewrite situation. Go did the best when considering multiple criteria that are particular to this situation, and it's worth explaining a few of them.

By far the most important aspect is that we need to keep the new codebase as compatible as possible, both in terms of semantics and in terms of code structure. We expect to maintain both codebases for quite some time going forward. Languages that allow for a structurally similar codebase offer a significant boon for anyone making code changes because we can easily port changes between the two codebases. In contrast, languages that require fundamental rethinking of memory management, mutation, data structuring, polymorphism, laziness, etc., might be a better fit for a ground-up rewrite, but we're undertaking this more as a port that maintains the existing behavior and critical optimizations we've built into the language. Idiomatic Go strongly resembles the existing coding patterns of the TypeScript codebase, which makes this porting effort much more tractable.

Go also offers excellent control of memory layout and allocation (both on an object and field level) without requiring that the entire codebase continually concern itself with memory management. While this implies a garbage collector, the downsides of a GC aren't particularly salient in our codebase. We don't have any strong latency constraints that would suffer from GC pauses/slowdowns. Batch compilations can effectively forego garbage collection entirely, since the process terminates at the end. In non-batch scenarios, most of our up-front allocations (ASTs, etc.) live for the entire life of the program, and we have strong domain information about when "logical" times to run the GC will be. Go's model therefore nets us a very big win in reducing codebase complexity, while paying very little actual runtime cost for garbage collection.

We also have an unusually large amount of graph processing, specifically traversing trees in both upward and downward walks involving polymorphic nodes. Go does an excellent job of making this ergonomic, especially in the context of needing to resemble the JavaScript version of the code.

Acknowledging some weak spots, Go's in-proc JS interop story is not as good as some of its alternatives. We have upcoming plans to mitigate this, and are committed to offering a performant and ergonomic JS API. We've been constrained in certain possible optimizations due to the current API model where consumers can access (or worse, modify) practically anything, and want to ensure that the new codebase keeps the door open for more freedom to change internal representations without having to worry about breaking all API users. Moving to a more intentional API design that also takes interop into account will let us move the ecosystem forward while still delivering these huge performance wins.

electroly

This is a great response but this is "why is Go better than JavaScript?" whereas my question is "why is Go better than C#, given that C# was famously created by the guy writing the blog post and Go is a language from a competitor?"

C# and TypeScript are Hejlsberg's children; C# is such an obvious pick that there must have been a monster problem with it that they didn't think could ever be fixed.

C# has all that stuff that the FAQ mentions about Go while also having an obvious political benefit. I'd hope the creator of said language who also made the decision not to use it would have an interesting opinion on the topic! I really hope we find out the real story.

As a C# developer I don't want to be offended but, like, I thought we were friends? What did we do wrong???

999900000999

I personally find Go miles easier than Rust.

Is this the ultimate reason,Go is fast enough without being overally difficult. I'm humbly open to being wrong.

While I'm here, any reason Microsoft isn't sponsoring a solid open source game engine.

Even a bit of support for Godot's C#( help them get it working on web), would be great.

Even better would be a full C# engine with support for web assembly.

https://github.com/godotengine/godot/issues/70796

sime2009

> we're undertaking this more as a port that maintains the existing behavior and critical optimizations we've built into the language. Idiomatic Go strongly resembles the existing coding patterns of the TypeScript codebase, which makes this porting effort much more tractable.

Cool. Can you tell us a bit more about the technical process of porting the TS code over to Go? Are you using any kind of automation or translation?

Personally, I've found Copilot to be surprisingly effective at translating Python code over to structurally similar Go code.

skybrian

It seems like, without mentioning any language by name, this answers "why not Rust" better than "why not C#."

I don't think Go is a bad choice, though!

breadwinner

So when can we expect Go support in Visual Studio? I am sold by Anders' explanation that Go is the lowest language you can use that has garbage collection!

fabian2k

I find the discussion about the choice quite interesting, and many points are very convincing (like the GC one). But I am a bit confused about the comparison between Go and C#. Both should meet most of the criteria like GC, control over memory layout/allocation and good support for concurrency. I'm curious what the weaknesses of C# for this particular use case were that lead to the decision for Go.

acomagu

Personally, I want to know why Go was chosen instead of Zig. I think Zig is really more WASM-friendly than Go, and it's much more similar to JavaScript than Rust is.

Memory management? Or a stricter type system?

mavelikara

Thanks for the thoughtful response!

HumanOstrich

[flagged]

noodletheworld

Go is quite difficult to embed in other applications due to the runtime.

What do you see as the future for use cases where the typescript compiler is embedded in other projects? (Eg. Deno, Jupyter kernels, etc.)

There’s some talk of an inter process api, but vague hand waving here about technical details. What’s the vision?

In TS7 will you be able to embed the compiler? Or is that not supported?

mappu

Go has buildmode=c-shared, which compiles your program to a C-style shared library with C ABI exports. Any first call into your functions initializes the runtime transparently. It's pretty seamless and automatic, and it'll perform better than embedding a WASM engine.

DanRosenwasser

We are sure there will be a way to embed via something like WebAssembly, but the goal is to start from the IPC layer (similar to LSP), and then explore how possible it will be to integrate at a tighter level.

_benton

Golang is actually pretty easy to embed into JS/TS via wasm. See esbuild.

curtisblaine

Esbuild is distributed as a series of native executables that are selectively installed by looking at arch and platform. Although you can build esbuild in wasm (and that's what you use when you run it in the browser), what you actually run from .bin in the CLI is a native executable, not wasm.

nine_k

Why embed it if you can run a process alongside yours and use efficient IPC? I suppose the compiler code should not be in some tight loop where an IPC boundary would be a noticeable slowdown. Compilation occurs relatively rarely, compared to running the compiled code, in things like Node / Deno / Bun / Jupyter. LSPs use this model with a pretty wasteful XML IPC, and they don't seem to feel slow.

wokwokwok

Because running a parallel process is often difficult. In most cases, the question becomes:

So, how exactly is my app/whatever supposed to spin up a parallel process in the OS and then talk to it over IPC? How do you shut it down when the 'host' process dies?

Not vaguely. Not hand wave "just launch it". How exactly do you do it?

How do you do it in environments where that capability (spawning arbitrary processes) is limited? eg. mobile.

How do you package it so that you distribute it in parallel? Will it conflict with other applications that do the same thing?

When you look at, for example, a jupyter kernel, it is already a host process launched and managed by jupyter-lab or whatever, which talks via network chatter.

So now each kernel process has to manage another process, which it talks to via IPC?

...

Certainly, there are no obvious performance reasons to avoid IPC, but I think there are use cases where having the compiler embedded makes more sense.

jillyboel

Javascript is also quite difficult to embed in other applications. So not much has changed, except it's no longer your language of choice.

demurgos

TypeScript compiles to JavaScript. It means both `tsc` and the TS program can share the same platform today.

With a TSC in Go, it's no longer true. Previously you only had to figure out how to run JS, now you have to figure out both how to manage a native process _and_ run the JS output.

This obviously matters less for situations where you have a clear separation between the build stage and runtime stage. Most people complaining here seem to be talking about environments were compilation is tightly integrated with the execution of the compiled JS.

aylmao

This is awesome. Thanks to you and all the TypeScript team for the work they put on this project! Also, nice to see you here, engaging with the community.

Porting to Go was the right decision, but part of me would've liked to see a different approach to solve the performance issue. Here I'm not thinking about the practicality, but simply about how cool it would've been if performance had instead been improved via:

- porting to OCaml. I contributed to Flow once upon a time, and a version of TypeScript in OCaml would've been huge in unifying the efforts here.

- porting to Rust. Having "official" TypeScript crates in rust would be huge for the Rust javascript-tooling ecosystem.

- a new runtime (or compiler!). I'm thinking here an optional, stricter version of TypeScript that forbids all the dynamic behaviours that make JavaScript hard to optimize. I'm also imagining an interpreter or compiler that can then use this stricter TypeScript to run faster or produce an efficient native binary, skipping JavaScript altogether and using types for optimization.

This last option would've been especially exciting since it is my opinion that Flow was hindered by the lack of dogfooding, at least when I was somewhat involved with the project. I hope this doesn't happen in the TypeScript project.

None of these are questions, just wanted to share these fanciful perspectives. I do agree Go sounds like the right choice, and and in any case I'm excited about the improvement in performance and memory usage. It really is the biggest gripe I have with TypeScript right now!

muglug

Not Daniel, but I've ported a typechecker from PHP to Rust (with some functional changes) and also tried working with the official Hack OCaml-based typechecker (a precursor to Flow).

Rust and OCaml are _maybe_ prettier to look at, but for the average TypeScript developer Go is a much more understandable target IMO.

Lifetimes and ownership are not trivial topics to grasp, and they add overhead (as discussed here: https://github.com/microsoft/typescript-go/discussions/411) that not all contributors might grasp immediately.

null

[deleted]

textlapse

I am curious why dotnet was not considered - it should run everywhere Go does with added NativeAoT too, so I am especially curious given the folks involved ;)

(FWIW, It must have been a very well thought out rationale.)

Edit: watched the revenant clip from the GH discussion- makes sense. Maybe push NativeAoT to be as good?

I am (positively) surprised Hejlsberg has not used this opportunity to push C#: a rarity in the software world where people never let go of their darlings. :)

jamescrowley

Discussion and video link here for anyone else interested: https://github.com/microsoft/typescript-go/discussions/411#d...

And lightly edited transcript here: https://github.com/microsoft/typescript-go/discussions/411#d...

neals

It was considered and tested, just not used in the end.

pjmlp

Why not AOT compiled C#, given the team's historical background?

dagw

There is an interview with Anders Hejlsberg here: https://www.youtube.com/watch?v=ZlGza4oIleY

The question comes up and he quickly glosses over it, but by the sound of it he isn't impressed with the performance or support of AOT compiled C# on all targeted platforms.

bokwoon

https://www.youtube.com/watch?v=10qowKUW82U

[19:14] why not C#?

Dimitri: Was C# considered?

Anders: It was, but I will say that I think Go definitely is -- it's, I'd say, the lowest-level language we can get to and still have automatic garbage collection. It's the most native-first language we can get to and still have automatic GC. In C#, it's sort of bytecode first, if you will; there is some ahead-of-time compilation available, but it's not on all platforms and it doesn't have a decade or more of hardening. It was not geared that way to begin with. Additionally, I think Go has a little more expressiveness when it comes to data structure layout, inline structs, and so forth. For us, one additional thing is that our JavaScript codebase is written in a highly functional style -- we use very few classes; in fact, the core compiler doesn't use classes at all -- and that is actually a characteristic of Go as well. Go is based on functions and data structures, whereas C# is heavily OOP-oriented, and we would have had to switch to an OOP paradigm to move to C#. That transition would have involved more friction than switching to Go. Ultimately, that was the path of least resistance for us.

Dimitri: Great -- I mean, I have questions about that. I've struggled in the past a lot with Go in functional programming, but I'm glad to hear you say that those aren't struggles for you. That was one of my questions.

Anders: When I say functional programming here, I mean sort of functional in the plain sense that we're dealing with functions and data structures as opposed to objects. I'm not talking about pattern matching, higher-kinded types, and monads.

[12:34] why not Rust?

Anders: When you have a product that has been in use for more than a decade, with millions of programmers and, God knows how many millions of lines of code out there, you are going to be faced with the longest tail of incompatibilities you could imagine. So, from the get-go, we knew that the only way this was going to be meaningful was if we ported the existing code base. The existing code base makes certain assumptions -- specifically, it assumes that there is automatic garbage collection -- and that pretty much limited our choices. That heavily ruled out Rust. I mean, in Rust you have memory management, but it's not automatic; you can get reference counting or whatever you could, but then, in addition to that, there's the borrow checker and the rather stringent constraints it puts on you around ownership of data structures. In particular, it effectively outlaws cyclic data structures, and all of our data structures are heavily cyclic.

(https://www.reddit.com/r/golang/comments/1j8shzb/microsoft_r...)

dimitropoulos

he went into more detail about C# in this one: https://youtu.be/10qowKUW82U?t=1154s

ayewo

Anders explained his reasoning in this interview (transcript):

https://github.com/microsoft/typescript-go/discussions/411#d...

uticus

this is the "official" response at this point, since it is in the FAQ linked in the OP

Cthulhu_

I'm not involved in the decisions, but don't C# applications have a higher startup time and memory usage? These are important considerations for a compiler like this that needs to start up and run fast in e.g. new CI/CD boxes.

For a daemon like an LSP I reckon C# would've worked.

rob74

Yes, in fact that's one of the main reasons given in the two linked interviews: Go can generate "real" native executables for all the platforms they want to support. One of the other reasons is (paraphrasing) that it's easier to port the existing mostly functional JS code to Go than to C#, which has a much more OOP style.

jabart

The C# compiler is written in C# and distributed to multiple platforms. Along with the JIT that runs on all kinds of devices.

Graph for the differences in Runtime, Runtime Trimmed, and AOT .NET.

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/n...

pjmlp

Native AOT exists, and C# has many C++ like capabilities, so not at all.

neonsunset

Not when compiled by NativeAOT. It also produces smaller binaries than Go and has better per-dependency scalability (due to metadata compression, pointer-rich section dehydration and stronger reachability analysis). This also means you can use F# too for this instead, which is excellent for langdev (provided you don't use printf "%A" which is incompatible which is a small sacrifice).

rob74

Seeing that Hejlsberg started out with Turbo Pascal and Delphi, and that Go also has a lot of Pascal-family heritage, he might hold some sympathy for Go as well...

pjmlp

Yes there is that irony, however when these kind of decisions are made, by folks with historical roots on how .NET and C# came to be, then .NET team cannot wonder why .NET keeps lagging adoption versus other ecosystems, on companies that aren't traditional Microsoft shops.

tgv

Not involved, but there's a faq in their repo, and this answers your question, perhaps, a bit: https://github.com/microsoft/typescript-go/discussions/411

pjmlp

Thanks, but it really doesn't clarify why a team with roots on the .NET ecosystem decided C#/Native AOT isn't fit for purpose.

doctor_phil

Link to interview with Anders. (linked from the thread as well) https://www.youtube.com/watch?v=10qowKUW82U&t=1154s

AshleysBrain

Well-optimized JavaScript can get to within about 1.5x the performance of C++ - something we have experience with having developed a full game engine in JavaScript [1]. Why is the TypeScript team moving to an entirely different technology instead of working on optimizing the existing TS/JS codebase?

[1] https://www.construct.net/en

do_not_redeem

Well-optimized JavaScript can, if you jump through hoops like avoiding object creation and storing your data in `Uint8Array`s. But idiomatic, maintainable JS simply can't (except in microbenchmarks where allocations and memory layout aren't yet concerns).

In a game engine, you probably aren't recreating every game object from frame to frame. But in a compiler, you're creating new objects for every file you parse. That's a huge amount of work for the GC.

AshleysBrain

I'd say that our JS game engine codebase is generally idiomatic, maintainable JS. We don't really do anything too esoteric to get maximum performance - modern JS engines are phenomenal at optimizing idiomatic code. The best JS performance advice is to basically treat it like a statically typed language (no dynamically-shaped objects etc) - and TS takes care of that for you. I suppose a compiler is a very different use case and may do things like lean on the GC more, but modern JS GCs are also amazing.

Basically I'd be interested to know what the bottlenecks in tsc are, whether there's much low-hanging fruit, and if not why not.

null

[deleted]

immibis

In other words you write asm.js, which is a textual form of WebAssembly that is also valid Javascript, and if your browser has an asm.js JIT compiler - which it doesn't because it was replaced by WebAssembly.

RyanCavanaugh

Our best estimate for how much faster the Go code is (in this situation) than the equivalent TS is ~3.5x

In a situation like a game engine I think 1.5x is reasonable, but TS has a huge amount of polymorphic data reading that defeats a lot of the optimizations in JS engines that get you to monomorphic property access speeds. If JS engines were better at monomorphizing access to common subtypes across different map shapes maybe it'd be closer, but no engine has implemented that or seems to have much appetite for doing so.

norswap

I used to work on compilers & JITs, and 100% this — polymorphic calls is the killer of JIT performance, which is why something native is preferable to something that JIT compiles.

Also for command-line tools, the JIT warmup time can be pretty significant, adding a lot to overall command-to-result latency (and in some cases even wiping out the JIT performance entirely!)

spankalee

> If JS engines were better at monomorphizing access to common subtypes across different map shapes maybe it'd be closer, but no engine has implemented that or seems to have much appetite for doing so.

I really wish JS VMs would invest in this. The DOM is full of large inheritance hierarchies, with lots of subtypes, so a lot of DOM code is megamorphic. You can do tricks like tearing off methods from Element to use as functions, instead of virtual methods as usual, but that quite a pain.

jerf

"Well optimized Javascript", and more generally, "well-optimized code for a JIT/optimizer for language X", is a subset of language X, is an undefined subset of language X, is a moving subset of language X that is moving in ways unrelated to your project, is actually multiple such subsets at a minimum one per JIT and arguably one per version of JIT compilers, and is generally a subset of language X that is extremely complicated (e.g., you can lose optimization if your arrays grow in certain ways, or you can non-locally deoptimize vast swathes of your code because one function call in one location happened to do one thing the JIT can't handle and it had to despecialize everything touching it as a result) such that trying to keep a lot of developers in sync with the requirements on a large project is essentially infeasible.

None of these things say "this is a good way to build a large compiler suite that we're building for performance".

jchw

Please note that compilers and game engines have extremely different needs and performance characteristics—and also that statements like "about 1.5x the performance of C++" are virtually meaningless out-of-context. I feel we've long passed this type of performance discussion by and could do with more nuanced and specific discussions.

johnfn

Who wants to spend all their time hand-tuning JS/TS when you can write the same code in Go, spend no time at all optimizing it, and get 10x better results?

grandempire

What kind of C++ and what kind of JS?

- C++ with thousands of tiny objects and virtual function calls? - JavaScript where data is stored in large Int32Array and does operations on it like a VM?

If you know anything about how JavaScript works, you know there is a lot of costly and challenging resource management.

dagw

Why is the TypeScript team moving to an entirely different technology

A few things mentioned in an interview:

Cannot build native binaries from TypeScript

Cannot as easily take advantage of concurrency in TypeScript

Writing fast TypeScript requires you to write things in a way that isn't 'normal' idiomatic TypeScript. Easier to onboard new people onto a more idiomatic codebase.

dboreham

The message I hear is: don't use JS, don't use async. Music to my ears.

Cthulhu_

While Go can be considered entirely different technology, I'd argue that Go is easy enough to understand for the vast majority of software developers that it's not too difficult to learn.

(disclaimer: I am a biased Go fan)

baq

It had been very explicitly designed with this goal. The idea was to make a simpler Java which is as easy as possible to deploy and as fast as possible to commute and by these measures is a resounding success.

jasonthorsness

I write a lot of Go and a decent amount of TypeScript. Was there anything you found during this project that you found particularly helpful/nice in Go, vs. TypeScript? Or was there anything about Go that increased the difficulty or required a change of approach?

bcherny

Fast dev tools are awesome and I am glad the TS team is thinking deeply about dev experience, as always!

One trade off is if the code for TS is no longer written in TS, that means the core team won’t be dogfooding TS day in and day out anymore, which might hurt devx in the long run. This is one of the failure modes that hurt Flow (written in OCaml), IMO. Curious how the team is thinking about this.

DanRosenwasser

Hey bcherny! Yes, dog-fooding (self-hosting) has definitely been a huge part in making TypeScript's development experience as good as it is. The upside is the breadth of tests and infrastructure we've already put together to watch out for regressions. Still, to supplement this I think we will definitely be leaning a lot on developer feedback and will need to write more TypeScript that may not be in a compiler or language service codebase. :D

rattray

Interesting! This sounds like a surprisingly hard problem to me, from what I've seen of other infra teams.

Does that mean more "support rotations" for TS compiler engineers on GitHub? Are there full-stack TS apps that the TS team owns that ownership can be spread around more? Will the TS team do more rotations onto other teams at MSFT?

pjc50

Ultimately the solution has to be breaking the browser monopoly on JS, via performance parity of WASM or some other route, so that developers can dogfood in performant languages instead across all their tooling, front end, and back end.

austin-cheney

First, this thread and article have nothing to do with language and/or application execution performance. It is only about the tsc compiler execution time.

Second, JavaScript already executes quickly. Aside from arithmetic operations it has now reached performance parity to Java and highly optimized JavaScript (typed arrays and an understanding of data access from arrays and objects in memory) can come within 1.5x execution speed of C++. At this point all the slowness of JavaScript is related to things other than code execution, such as: garbage collection, unnecessary framework code bloat, and poorly written code.

That being said it isn't realistic to expect measurably significant faster execution times by replacing JavaScript with a WASM runtime. This is more true after considering that many performance problems with JavaScript in the wild are human problems more than technology problems.

Third, WASM has nothing to do with JavaScript, according to its originators and maintainers. WASM was never created to compete, replace, modify, or influence JavaScript. WASM was created as a language ubiquitous Flash replacement in a sandbox. Since WASM executes in an agnostic sandbox the cost to replace an existing runtime is high since an existing run time is already available but a WASM runtime is more akin to installing a desktop application for first time run.

sebzim4500

How do you reconcile this view with the fact that the typescript team rewrote the compiler in Go and it got 10x faster? Do you think that they could have kept in in typescript and achieved similar performance but they didn't for some reason?

mmcnl

Very short, succinct and informative comment. Thank you.

bloomingkales

Are you looking for non-browser performance such as 3d? I see no case that another language is going to bring performance to the DOM. You'd have to be rendering straight to canvas/webgl for me to believe any of this.

axkdev

They should write a typescript-to-go transpiler (in typescript) , so that they can write their compiler in typescript and use typescript to transpile it to go.

jillyboel

The issue with Flow is that it's slow, flaky and has shifted the entire paradigm multiple times making version upgrades nearly impossible without also updating your dependencies, IF your dependencies adopted the new flow version as well. Otherwise you're SOL.

As a result the amount of libraries that ship flow types has absolutely dwindled over the years, and now typescript has completely taken over.

matclayton

Our experience is the opposite, we have a pretty large flow typed code base, and can do a full check in <100ms. When we converted to TS (decided not to merged) we saw typescript was in the multiple minute mark. It’s worth checking out LTI and how the typing on boundaries, enables flow to parallelize and give very precise error messages compared to TS. The third party lib support is however basically dead, except the latest versions of flow are starting to enable ingestion of TS types, so that’s interesting.

kevlened

For previous attempts at a faster tsc, but in rust, see:

1. https://github.com/dudykr/stc - Abandoned (https://github.com/swc-project/swc/issues/571#issuecomment-1...)

2. https://github.com/kaleidawave/ezno - In active development. Does not have the goal of 1:1 parity to tsc.

smarx007

I think Deno and Bun are the two successful attempts at a faster tsc :)

keturakis

Both Deno and Bun still use current tsc for type checking

madjam002

They just strip types and don’t do any type checking

zoogeny

I notice this time and time again: projects start with a flexible scripting language and a promise that the performance will be sufficient. I mean, JS is pretty performant as scripting languages go and it is hard to think of any language runtimes that get more attention than the browser VMs. And generally, 90% of the things people do will run sufficiently fast in that VM.

Yet projects inevitably get to the stage where a more native representation wins out. I mean, I can't think of a time a high profile project written in a lower level representation got ported to a higher level language.

It makes me think I should be starting any project I have in the lowest level representation that allows me some ergonomics. Maybe more reason to lean into Zig? I don't mean for places where something like Rust would be appropriate. I mean for anything I would consider using a "good enough" scripting language.

It honestly has me questioning my default assumption to use JS runtimes on the server (e.g. Node, deno, bun). I mean, the benefit of using the same code on the server/client has rarely if ever been a significant contributor to project maintainability for me. And it isn't that hard these days to spin up a web server with simple routing, database connectivity, etc. in pretty much any language including Zig or Go. And with LLMs and language servers, there is decreasing utility in familiarity with a language to be productive.

It feels like the advantages of scripting languages are being eroded away. If I am planning a career "vibe coding" or prompt engineering my way into the future, I wonder how reasonable it would be to assume I'll be doing it to generate lower level code rather than scripts.

throwitaway1123

> I mean, I can't think of a time a high profile project written in a lower level representation got ported to a higher level language.

Prisma is currently being rewritten from Rust to TypeScript: https://www.prisma.io/blog/rust-to-typescript-update-boostin...

> Yet projects inevitably get to the stage where a more native representation wins out.

I would be careful about extrapolating the performance gains achieved by the Go TypeScript port to non-compiler use cases. A compiler is perhaps the worst use case for a language like JS, because it is both (as Anders Hejlsberg refers to it) an "embarassingly parallel task" (because each source file can be parsed independently), but also requires the results of the parsing step to be aggregated and shared across multiple threads (which requires shared memory multithreading of AST objects). Over half of the performance gains can be attributed to being able to spin up a separate goroutine to parse each source file. Anders explains it perfectly here: https://www.youtube.com/watch?v=ZlGza4oIleY&t=2027s

We might eventually get shared memory multithreading (beyond Array Buffers) in JS via the Structs proposal [1], but that remains to be seen.

[1] https://github.com/tc39/proposal-structs?tab=readme-ov-file

zoogeny

I think the Prisma case is a bit of a red herring. First, they are using WASM which itself is a a low-level representation. Second, the performance gains appear primarily in avoiding the marshalling of data from JavaScript into Rust (and back again I presume). Basically, if the majority of your application is already in JavaScript and expects primarily to interact with other code written in JavaScript, it usually doesn't make sense to serialize your data, pass it to another runtime for some processing, then pass the result back.

As for the "compilers are special" reasoning, I don't ascribe to it. I suppose because it implies the opposite: something (other than a compiler) is especially suited to run well in a scripting language. But the former doesn't imply the later in reality and so the case should be made independently. The Prisma case is one: you are already dealing with JavaScript objects so it is wise to stay in JavaScript. The old cases I would choose the scripting language (familiarity, speed of adding new features, ability to hire a team quickly) seem to be eroding in the face of LLMs.

throwitaway1123

> First, they are using WASM which itself is a a low-level representation.

WASM is used to generate the query plan, but query execution now happens entirely within TypeScript, whereas under the previous architecture both steps were handled by Rust. So in a very literal sense some of the Rust code is being rewritten in TypeScript.

> Basically, if the majority of your application is already in JavaScript and expects primarily to interact with other code written in JavaScript, it usually doesn't make sense to serialize your data, pass it to another runtime for some processing, then pass the result back.

My point was simply to refute the assertion that once software is written in a low level language, it will never be converted to a higher level language, as if low level languages are necessarily the terminal state for all software, which is what your original comment seemed to be suggesting. This feels like a bit of a "No true Scotsman" argument: https://en.wikipedia.org/wiki/No_true_Scotsman

> As for the "compilers are special" reasoning, I don't ascribe to it.

Compilers (and more specifically lexers and parsers) are special in the sense that they're incredibly well suited for languages with shared memory multithreading. Not every workload fits that profile.

> The old cases I would choose the scripting language (familiarity, speed of adding new features, ability to hire a team quickly) seem to be eroding in the face of LLMs.

I'm not an AI pessimist, but I'm also not an AI maximalist who is convinced that AI will completely eliminate the need for human code authoring and review, and as long as humans are required to write and review code, then those benefits still apply. In fact, one of the stated reasons for the Prisma rewrite was "skillset barriers". "Contributing to the query engine requires a combination of Rust and TypeScript proficiency, reducing the opportunity for community involvement." [1]

[1] https://www.prisma.io/blog/from-rust-to-typescript-a-new-cha...

null

[deleted]

melbourne_mat

I think it's smart to start with a high level language which should reduce development time, prove the worth of the application, then switch to a lower level language later.

What was that saying again? Premature optimisation is the root of all evil

vacuity

https://news.ycombinator.com/item?id=29228427

A thread going into what Knuth meant by that quote that is usually shortened to "premature optimization is the root of all evil". Or, to rephrase it: don't tire yourself out climbing for the high fruit, but do not ignore the low-hanging fruit. But really I don't even see why "scripting languages" are the particular "high level" languages of choice. Compilers nowadays are good. No one is asking you to drop down to C or C++.

sapiogram

> I mean, I can't think of a time a high profile project written in a lower level representation got ported to a higher level language.

Software never gets rewritten in a higher level language, but software is constantly replaced by alternatives. First example that comes to mind is Discord, an Electron app that immediately and permanently killed every other voice client on the market when it launched.

zoogeny

Yes, scripting replacements often usurp existing ossified alternatives. And there is some truth that a higher level language gave some leverage to the developers. That is why I mentioned the advent of LLM based coding assistants and how this may level the playing field.

If we assume that coding assistants continue to improve as they have been and we also assume that they are able to generate lower level code on par with higher level code, then it seems the leverage shifts away from "easy to implement features" languages to "fast in most contexts" languages.

Only time will tell, of course. But I wonder if we will see a new wave of replacements from Electron based apps to LLM assisted native apps.

wrs

It’s a little more nuanced though — I doubt the audio processing in Discord is written in JavaScript. (But I haven’t looked!)

timeon

Isn't most of Discord backend Rust and Go?

colonelspace

> Discord ... immediately and permanently killed every other voice client on the market

Do you mean voice clients like FaceTime, Zoom, Teams, and Slack?

ZeWaka

They're talking about TeamSpeak, Vent, Mumble, and Skype.

timeon

Sure but that comment was mostly about backend. If Discord used js/ts for backend they wouldn't replace anyone.

xandrius

I don't think the success of Discord is due to it being written in Electron. Or is it?

jonathanlydall

I game very little these days, but have run mumble, ventrillo and teamspeak in the past and the problem was always the friction in onboarding people onto them, you’d have to exchange host, port, password at best, or worse, explain how to download, install and use.

Discord can run from a browser, making onboarding super easy. The installable app being in Electron makes for minimal (if any) difference between it and the website.

In summary, running in the web browser helps a lot, and Electron makes it very easy for them to keep the browser version first class.

As an added bonus, they can support Linux, Windows and macOS equally well.

I would say it helps as without Electron, serving all the above with equal feature parity just would have been too expensive or slow and perhaps it just wouldn’t have been as frictionless for all types of new users like it is.

ironmagma

Inevitably? Well, the promise of using something less efficient in terms of performance is that it will be more efficient in terms of development. Many times projects fail because they optimize too early and never built the features they needed to or couldn’t iterate fast enough to prove value and die. So if the native version is better but failed, it’s not so inevitable that it will get to that stage.

zoogeny

Right, which is my point about LLM code assistants. If you did have two cases in the past: native but slow to add features so the project eventually dies vs. scripted but performance is bad enough it eventually needs to be rewritten. (Of course, this is a false dichotomy but I'm playing into your scenario).

Now we may have a new case: native but fast to add features using a code assist LLM.

If that new case is a true reflection of the near future (only time will tell) then it makes the case against the scripted solution. If (and only if) you could use a code assist LLM to match the feature efficiency of a scripting language while using a native language, it would seem reasonable to choose that as the starting point.

ironmagma

That’s an interesting idea. It’s amazing how far we’ve come without essentially any objective data on how much these various methodologies (e.g. using a scripting language) improve or worsen development time.

The adoption of AI Code Assistance I am sure will be driven similarly anecdotally, because who has the time or money to actually measure productivity techniques when you can just build a personal set of superstitions that work for you (personally) and sell it? Or put another way, what manager actually would spend money on basic science?

pier25

> It honestly has me questioning my default assumption to use JS runtimes on the server (e.g. Node, deno, bun).

The JS runtimes are fine for the majority of use cases but the ecosystem is really the issue IMO.

> the benefit of using the same code on the server/client has rarely if ever been a significant contributor to project maintainability for me

I agree and now with OpenAPI this is even less of an argument.

Tadpole9181

The JS `tsc` type checks the entire 1.5 million line VS Code source in 77s (non-incremental). 7s is a lot better and will certainly imrpove DX - which is their goal - but I don't see how that's "insufficient".

The trade-off is that the team will have to start dealing with a lot of separate issues... How do tools like ESLint TS talk to TSC now? How to run this in playground? How to distribute the binaries? And they also lose out on the TS type system, which makes their Go version rely a little more on developer prowess.

This is an easy choice for one of the most fundamental tools underlaying a whole ecosystem, maintained by Microsoft and one of the developers of C# itself, full-time.

Other businesses probably want to focus on actually making money by leading their domain and easing long-term maintenance.

pjmlp

Even though I have my considerations regarding Go, I love that they picked Go instead of the fashion to go Rust that seems to be the norm now.

A compiled managed language is much better approach for userspace applications.

Pity that they didn't go with AOT compiled .NET, though.

pjc50

> Pity that they didn't go with AOT compiled .NET, though.

Yeah. It seems to be unfashionable somewhat even within Microsoft.

(edit: it seems to be you and me and barely anyone else on HN advocating for C#)

nwah1

Also, this is surprising because this was presented and led by Anders Hejlsberg, who is the creator of both C# and Typescript.

If anyone should have picked C# it would be him.

dagw

Hejlsberg seemed quite negative when it came to cross platform AOT compiled C# in several comments he's made, hinting at problems with both performance and maturity on certain platforms.

Chyzwar

I think the main thing is that they are porting, not re-writing. Current tsc is functional by nature and that's makes go better fit.

xanth

I think the much larger ask C# couldn't answer is the "expressive" access to low level struct layout[1]

[1] https://www.youtube.com/watch?v=10qowKUW82U&t=769s

atonse

This was also surprising to me – C# is a really awesome and modern language.

I happened to be doing a lot of C# and .NET dev when all this transition was happening, and it was very cool to be able to run .NET in Linux. C# is a powerful language with great and constantly evolving ideas in it.

But then all the stuff between the runtimes, API surfaces, Core vs Framework, etc all got extremely confusing and off-putting. It was necessary to bring all these ecosystems together, but I wonder if that kept people away for a bit? Not sure.

pjmlp

All Azure contributions to CNCF are using a mix of Go and Rust, mostly.

Here is a kind of weird, given the team.

jjice

If I recall in an article from a while back, the idea was originally rust, but the current compiler design had lots of references shared references that would make the port to rust a lot of work.

pjmlp

Personally, Rust only makes sense in scenarios that automatic memory management of any kind is either unwanted, or it is a quixotic battle making the target group think otherwise.

OS kernels, firmware, GPGPU,....

If it is the ML inspired type system, there are plenty of options among compiled managed languages, true Go isn't really on that camp, but whatever.

jjice

I'd love a language that is a GC'd like go, but with the ML inspired type system, and still an imperative language. OCaml seems to be the closest thing to Rust in that regard, but it's not imperative.

cheepin

Rust memory management is automatic. Object destructors run when the object exits scope without needing explicit management by the programmer

surajrmal

Or possibly you want to use a language you're familiar with in adjacent spaces (eg tools) or you want to tackle concurrency bugs more directly. There is more to rust than it's

dist1ll

Dealing with references you typically find in a compiler is not a problem for Rust. Arena allocation and indices are your friend.

johnmw

I wonder if this project can easily be integrated into Deno (built mainly in Rust)?

zamalek

He also mentioned doing a line-for-line port. Assuming you could somehow manage that, you'd probably end up with something slower than JS (not entirely a joke). I'm a rust fanboy, but have to concede that Go was the be t choice here.

If it was a fresh compiler then the choice would be more difficult.

nightpool

Hejlsberg discusses the decision not to use C# here: https://www.youtube.com/watch?v=10qowKUW82U&t=1154s

bichiliad

There are some external projects that have tried to port tsc to native. stc[0], for instance, was one. Iirc it started out in Go since it had a more comparable type system (they both use duck typing) making it easier to do one-to-one conversions of code from one language to the other. I’m not totally sure why it ended up pivoting to rust.

[0]: https://github.com/dudykr/stc

ninkendo

> I love that they picked Go instead of the fashion to go Rust

This seems super petty to me. Like, if at the end of the day you get a binary that works on your OS and doesn’t require a runtime, why should you “love” that they picked one language over another? It’s exactly the same outcome for you as a user.

I mean, if you wanted to contribute to the project and you knew go better than rust, that would make sense. But sounds like you just don’t like rust because of… reasons, and you’re just glad to see rust “fail” for their use case.

rat9988

>Pity that they didn't go with AOT compiled .NET, though.

I was trying ot push .net as our possible language for somehow high performance executables. Seeing this means I'll stop trying to advocate for it. If even this team doesn't believe in it.

criddell

That makes sense if your project has similar constraints and requirements.

I like when Microsoft doesn't pretend that their technologies are the right answer for every problem.

madeofpalk

One unrelated team at Microsoft doesn't 'believe' in .NET is enough to make you change direction?

9rx

More specifically, the guy who created C# doesn't believe in it (for this particular project).

But, of course, that is not unusual. There is no language in existence that is best suited to every project out there.

surajrmal

They cited code style and porting as reasons to use go over c#, not performance.

rs186

Also cross platform support

rat9988

I didn't say it was very performance critical, go and c# are both good enough for us in this regard. The problem is that, when evaluating the whole thing, they decided against c#, that is problematic here.

agumonkey

I was asking myself the same questions.

ah, answers below: https://news.ycombinator.com/item?id=43333296

tinco

It's not just a pity, it's very surprising. In my eyes Go is a direct competitor of C#. Whenever you pick Go for a project, C# should have been a serious consideration. Hejlsberg designed C# and that a team that he's an authority figure in would opt to use Go, a language which frankly I would not consider to build a compiler in is astounding.

Not saying that in a judgemental way, I'm just genuinely surprised. What does this say about what Hejlsberg thinks of C# at the moment? I would assume one reason they don't pick C# is because it's deeply unpopular in the open source world. If Microsoft was so successful in making Typescript popular for open source work, why can't they do it for C#?

I have not opted to use C# for anything significant in the past decade or so. I am not 100% sure why, but there's always been something I'd rather use. Whether that's Go, Rust, Ruby or Haskell. I always enjoyed working in C#, I think it's a well designed and powerful language even if it never made the top of my list recently. I never considered that there might be something so fundamentally wrong with it that not even Hejlsberg himself would use it to build a Typescript compiler.

What's wrong with C#?

duckerude

Anders Hejlsberg explains here: https://youtu.be/10qowKUW82U?t=1154. TL;DW:

- C# is bytecode-first, Go targets native code. While C# does have AOT capabilities nowadays this is not as mature as Go's and not all platforms support it. Go also has somewhat better control over data layout. They wanted to get as low-level as possible while still having garbage collection.

- This is meant to be something of a 1:1 port rather than a rewrite, and the old code uses plain functions and data structures without an OOP style. This suits Go well while a C# port would have required more restructuring.

neonsunset

This is shockingly out-of-date statement by Anders.

I'm not sure what's going on, I guess he's just not involved with the runtime side of .NET at all to actually know where the capability sits circa 2024/2025. But really, it's a terrible situation to be in. Especially just how worse langdev UX in Go is compared to C#, F# or Rust. No one would've batted an eye if either of those was used.

dustedcodes

C# has become a poor jack of all trades, trying to be Java, Go and F# at the same time and actually being a shity poor version of all of them. On top of that .NET has become a very enterprisey bloatware. In all honesty, I'm not surprised that they went with Go, as it has a clear identity, a clear use-case which it caters for extremely well and doesn't lose focus with trying to be too many other unrelated things at the same time.

Maybe it's time to stop eating everything that Microsoft sales folks/evangelists spoon feed you and wake up to the fact that only because people paid by Microsoft to roll the drum about Microsoft products telling you that .NET and C# is oh so good and the best in everything, maybe it's not actually that credible?

Look at the hard facts. Every single product which Microsoft has built that actually matters (e.g. all their Azure CNCF stuff, Dapr, now this) is using non Microsoft languages and technologies.

You won't see Blazor being used by Microsoft or the 73rd reinvention of ASP.NET Core MVC Minimal APIs Razor Pages Hocus Pocus WCF XAML Enterprise (TM) for anything mission critical.

Guillaume86

It seems it's because AOT is a bit of a second fiddle in the dotnet ecosystem and native is a top priority for their case. After hearing the reasoning ( https://youtu.be/ZlGza4oIleY?si=1GKSX61AF20VQr-G&t=1000 ) I don't blame them for choosing Go.

dimgl

C# needs an interpreter (.NET runtime) while Go compiles down to a binary. And the toolchain allows you to compile for other architectures fairly easily.

So that could be a fundamental reason why.

rat9988

The grand parent was talking about AOT.

bitwize

.NET has AOT compilation now. There really is no excuse, especially when you consider that C# has a pretty decent type system and Go has an ad-hoc, informally specified, bug-ridden, slow implementation of half of a decent type system.

wesbos

We had Daniel and Anders on the podcast to talk about the how and why of the native port if anyone is looking for an in-depth discussion → https://www.youtube.com/watch?v=ZlGza4oIleY

grantwu

> By far the most important aspect is that we need to keep the new codebase as compatible as possible, both in terms of semantics and in terms of code structure. We expect to maintain both codebases for quite some time going forward. Languages that allow for a structurally similar codebase offer a significant boon for anyone making code changes because we can easily port changes between the two codebases. In contrast, languages that require fundamental rethinking of memory management, mutation, data structuring, polymorphism, laziness, etc., might be a better fit for a ground-up rewrite, but we're undertaking this more as a port that maintains the existing behavior and critical optimizations we've built into the language. Idiomatic Go strongly resembles the existing coding patterns of the TypeScript codebase, which makes this porting effort much more tractable.

--https://github.com/microsoft/typescript-go/discussions/411

I haven't looked at the tsc codebase. I do currently use Golang at my job and have used TypeScript at a previous job several years ago.

I'm surprised to hear that idiomatic Golang resembles the existing coding patterns of the tsc codebase. I've never felt that idiomatic code in Golang resembled idiomatic code in TypeScript. Notably, sum types are commonly called out as something especially useful in writing compilers, and when I've wanted them in Golang I've struggled to replace them.

Is there something special about the existing tsc codebase, or does the statement about idiomatic Golang resembling the existing codebase something you could say about most TypeScript codebases?

jchw

> I'm surprised to hear that idiomatic Golang resembles the existing coding patterns of the tsc codebase. I've never felt that idiomatic code in Golang resembled idiomatic code in TypeScript.

To be fair, they didn't actually say that. What they said was that idiomatic Go resembles their existing patterns. I'd imagine what they mean by that is that a port from their existing patterns to Go is much closer to a mechanical 1:1 process than a port to Rust or C#. Rust is the obvious choice for a fully greenfield implementation, but reorganizing around idiomatic Rust patterns would be much harder for most programs that are not already written in a compatible style. e.g. For Rust programs, the precise ownership and transfer of memory needs to be modelled, whereas Go and JS are both GC'd and don't require this.

For a codebase that relies heavily on exception handling, I can imagine a 1:1 port would require more thought, but compilers generally need to have pretty good error recovery so I wouldn't be surprised if tsc has bespoke error handling patterns that defers error handling and passes around errors as values a lot; that would map pretty well to Go.

Most TypeScript projects are very far away from compiler code, so that this wouldn't resemble typical TypeScript isn't too surprising. Compilers written in Go also don't tend to resemble typical Go either, in fairness.

nathanrf

I'm not involved in this rewrite, but I made some minor contributions a few years ago.

TSC doesn't use many union types, it's mostly OOP-ish down-casting or chains of if-statements.

One reason for this is I think performance; most objects are tagged by bitsets in order to pack more info about the object without needing additional allocations. But TypeScript can't really (ergonomically) represent this in the type system, so that means you don't get any real useful unions.

A lot of the objects are also secretly mutable (for caching/performance) which can make precise union types not very useful, since they can be easily invalidated by those mutations.

dcre

In the embedded video they show some of the code side by side and it is just a ton of if statements.

https://youtu.be/pNlq-EVld70?si=UaFDVwhwyQZqkZrW&t=323

1oooqooq

to be fair, there's not many ways to implement a token matcher.

though looking at that flood of loose ifs+returns, i kinda wish they used rust :)

dcre

I’d guess Rust compile times weren’t worth it if they weren’t going to be taking advantage of the type system in interesting ways.

0xcb0

After years of PHP, I came to typescript nearly 4 years ago (for web front and backend development). All I can say is that I really enjoy using this programming language. The type system is just about enough to be helpful, and not too much to be in your way. Compiling the codebase is quite fast, compared to other languages. With a 10x, it will be so much fun to code.

Never been a big fan of MS, but must say that typescript is well done imho. thanks for it and all the hard work!

zem

microsoft has historically been great at programming languages. qbasic, visual basic, c#, and f# are all excellent.

dimgl

I'm really surprised by this visceral reaction to not choosing Rust. Go is a great language and I'd choose it for a majority of projects over Rust just based off of the simplicity of the language and the ability to spin up developers on it quickly. Microsoft is a big corporation.

Why _not_ use Go?

homebrewer

> Why _not_ use Go?

Because of its truly primitive type system, and because Microsoft already has a much better language — C#, which is both faster and can be more high level and more low-level at the same time, depending on your needs.

I am a complete nobody to argue with the likes of Hejlsberg, but it feels like AOT performance problems could be solved if tsc needed it, and tsc adoption of C# would also help push C#/.NET adoption. Once again, Microsoft proves that it's a bunch of unrelated companies at odds with each other.

triceratops

I'm inclined to trust the judgement of Hejlsberg, the chief architect of C#, in this matter.

9rx

> Because of its truly primitive type system

That is the main reason they gave for why they those chose Go. The parent asked "Why _not_ use Go?"

nipah

This is not "the main reason", lol, it was never stated as such. The type system could be way more powerful and, having the same general features they would probably had still picked it up.

subarctic

So they like having all the footguns?

dmix

Do we need to have these conversations weekly?

dimgl

I wouldn't be asking if there wasn't a visceral reaction from Rust devs. I must have missed previous discussions on other threads.

J_Shelby_J

Is this visceral reaction in the room with us now?

Edit: I have reached the bottom of the thread and still have not seen this visceral reaction mentioned by the OP.

jujadjwdfs

Not sure if this point was brought up but I think it's worth considering.

If the Typescript team were to go with Rust or C# they would have to contend with async/await decoration and worry about starvation and monopolization.

Go frees the developer from worrying about these concerns.

haxiomic

Sounds like they're automatically generating Go code from ts in some amount [0]. I wonder if they will open the transpilation effort, in this way you'd create a path for other TypeScript projects to generate fast native binaries

Opened discussion [1]

- [0] https://github.com/microsoft/typescript-go/discussions/410

- [1] https://github.com/microsoft/typescript-go/discussions/467

jakebailey

The automatic generation was mainly a step to help with manual porting, since it requires so much vetting and updating for differences in data layout; effectively all of the checker code Anders ported himself!

maxloh

It seems that they port the code manually, probably with the help of LLMs.

https://github.com/microsoft/typescript-go/commits?after=dad...

dustedcodes

Meanwhile .NET developers are still waiting for Microsoft to use their own "inventions" like Blazor, .NET MAUI, Aspire, etc. for anything meaningful. Bless them.

zuhsetaqi

Aspire is made with Blazor