Anders 当年为什么不选 C#
2025 年初,微软宣布把 TypeScript 编译器从 TypeScript 自己移植成原生代码,语言选的是 Go。
这个消息在 .NET 圈子里多少有点微妙。Anders Hejlsberg 是 C# 的首席设计师,也是 TypeScript 之父,结果亲儿子要换新家,选的却不是另一个亲儿子。官方 Why Go? 的讨论帖下面,C# 相关的问题被问了一遍又一遍。
后来 Anders 亲自下场,回了一段很长的话。核心是这几句:
In a green field, this would have been a totally different conversation. But this was not a green field - it's a port of an existing codebase with 100 man-years of investment.
如果是从零开始,那会是完全不同的讨论。但这不是从零开始------这是对一个已经投入了 100 人年的代码库做移植。

他还提到,团队评估过很多语言、做过好几个原型,其中就有 C#。最后选 Go,是因为 TypeScript 编译器的代码里全是函数和数据结构,几乎没有类,而地道的 Go 代码看起来跟它几乎一模一样,移植可以做到一一对应,甚至能大量自动化。
换句话说,这次是移植(port) ,不是重写(rewrite)。只有逐行移植,才能保住现有编译器的语义、算法和每一个报错行为,而全世界的项目都依赖这些行为。
于是,一支世界顶级的编译器团队,花了一年多时间,把这份 100 人年的家底一行一行地搬进了 Go。
这个决定我是服气的。只是心里总有个念头:如果当年选的是 C#,现在会怎样?
以前这个问题没法回答,因为没人会再花一年多时间去验证一个假设。但 2026 年,情况变了。
42 万美元的 Rust
先说一个不是 C# 的故事。
YouTube 网红 Theo(就是 T3 Stack 那位)做了 ts-rust:用大模型把 TypeScript 的 Go 版编译器整个移植成 Rust。README 第一句就很直接:
It cost over 420,000 in tokens to do it, but you could probably have done it for \~20k.
他先用 OpenAI 的模型跑了几个月,烧掉了 40 多万美元的 API 等价额度,写了 130 多万行 Rust,兼容性一直卡在 84% 左右。后来换成 Claude Opus 5.5 从头开始,10 小时出了能用的 v0,两周、约 2.4 万美元把它做完了。

看到这个数字,我的第一反应是:

他还专门拍了一期视频,标题就叫"You can rewrite typescript in rust for $420k":

视频里讲了一个很有意思的细节(14:00 前后):Opus 并没有"用 Rust 重新设计",而是把 Go 版一行一行、忠实地搬了过去,连 Go 的标准库和 goroutine 都在 Rust 里重新实现了一遍。这正是微软团队当年从 JS 到 Go 的做法------先逐行移植,再慢慢打磨。
所以第一版 ts-rust 其实比 Go 版慢两到三倍。Theo 随后给 AI 定了一个目标:"每一项都至少快两倍",然后让它自己跑了两周。
结果是:ts-rust 不光跑通了 18 万多个移植过来的 Go 测试,速度还反超了官方 Go 版。按 Theo 的说法,这 2.4 万美元的 API 等价开销,实际走的是 Claude 订阅,折算下来也就 500 美元上下。
这件事说明了三点:
- 用 AI 把 TypeScript 编译器整个搬到另一门语言,已经不是天方夜谭。
- 方法比钱重要。同样的目标,走错路烧 40 万,走对路两万多。
- 顺序很重要:先逐行移植、把行为对齐,再谈优化。第一版比 Go 慢两三倍,一点都不丢人。
还有一个细节:Theo 在视频开头就说了,整个项目他一行代码都没看过。
回头再看 Anders 的理由
用今天的眼光再读一遍 Anders 那段话,会发现它一半被 AI 改写了,一半反而更成立了。
被改写的那一半:选 Go,是因为原代码和地道的 Go 长得一样,人工移植最省事。这个理由的前提是"移植主要靠人"。可 Go 和 Rust 差得够远了------一个有 GC,一个没有;一个随手开 goroutine,一个要跟借用检查器较劲------AI 照样逐行搬了过去,而作者一行没看。下面会看到,搬到 C# 的成本更是低到一两百美元。"语言长得像不像",已经不再是决定性的因素。
更成立的那一半:所有这些 AI 移植,起点都是 Go 版,而不是原来的 JS 版。Theo 自己也说,让 AI 直接从 JS 移植到 Rust 是做不成的,从 Go 出发却相对轻松。而"一行不看"之所以敢这么干,靠的是测试:ts-rust 跑通了 18 万多个移植过来的 Go 测试。那 100 人年并没有消失,它变成了 Go 版的代码和测试,成了今天所有 AI 移植的"标准答案"。
所以,Anders 当年的选择没错,而且正是那次人工逐行移植,给后来的 AI 移植铺好了路。只是门槛从"一支顶尖团队干一年多"变成了"一个订阅套餐跑两周"。
那 C# 呢?
三位 C# 选手
我知道的用 C# 做这件事的项目,目前有三个,按时间顺序说。
1. typescript-csharp(作者 token,2026 年 7 月起)
三个里最早开工的。用 Claude Code 做的一比一移植,.NET 10 NativeAOT。前期主要用 Claude Fable 5 和 Opus 4.8,commit 的 Co-authored-by 里都能看到;最近换成了他自己做的开源 AI 开发工具 NextCoWork,模型是 Opus 5.5,所以新的 commit 里没有 Co-authored-by 了。它拿官方仓库的 49,321 个基线文件当标准,逐字节比对,进度表一路记得很细。

2. hez2010/TypeScript(作者 hez2010,9 月 22 日起)
用的是 200 美元一个月的 Codex 套餐,模型是 GPT-6.1 Sol。直接在官方仓库的 fork 里做,大约两周把编译器、构建、增量编译、LSP 一路做完,并且把 C# 版设成了默认编译器。

它和另外两个思路不太一样:不是逐文件翻译,而是按职责重新切分,允许多个 Go 函数合并成一个 C# 实现,C# 代码量只有 Go 版的一半左右。为了保证这样改了之后行为还一样,它把每一处和 Go 的差异都记进一本"差异台账",验证做得极其细致。本文的性能测试工具也是从这个仓库里来的。
国庆期间,公众号上已经有一篇文章详细介绍过这个项目:

评论区的画风大概是这样的:
C# 做编译器的确不行。
搞出来发现不如 Go......那也没事,毕竟也没人在意这个项目。
3. stepwisehq/typescript-dotnet(10 月 8 日起)
作者自报家门:"I'm a C# Stan."(我是 C# 铁粉)。他坦言是被 Theo 那个"40 万美元"刺激到了,回了一句 "Hold my 🍺"。README 里写的总成本:约 75--100 美元。

他用 Codex(Astra 6 做总调度,6.1 Sol 做子任务),一开始就给 AI 定了规矩:尽可能贴近 Go 源码,函数边界、类名、文件结构都保持一致。
检查器移植完之后,他才开始专门做性能。当时用它检查 VS Code 的源码,Go 版分配了约 5.1 GB 内存,C# 版分配了约 56 GB,耗时是 Go 的 6 倍多。一轮优化下来,引入了一个模仿 Go slice 语义的 Slice<T>,以及一个直接操作字节的 ByteStringView,C# 代码在长相上仍然跟 Go 很像。按 README 的说法,最后编译 VS Code(含 emit)只比 Go 慢 8.5% 左右。
他在 README 里也说了句很实在的话:地道的 Go 在内存分配和共享上本来就比地道的 C# 更高效。
跑个分
怎么测的
测试工具用的是 hez2010 仓库 typescript-cs 分支里的 csharp/tools/performance-cli.mjs(我加了个参数用来换挑战者,见文末),它的做法我很认可:
- 成对、交替:每一轮 Go 和挑战者各跑一次,先后顺序轮换,抵消机器状态的波动。
- 每次都是新进程 :包含进程启动时间,和你在命令行里敲
tsc的体验一致。 - 输出必须逐字节相同 :生成的
.js、.d.ts、诊断信息只要跟 Go 版有一个字节不同,整轮测试直接失败。所以下面每个项目,在这些用例上的输出都和官方 Go 版完全一致。 - 3 次预热,5 组有效样本,取中位数。
测试用例是工具自带的几个合成项目:
| 用例 | 内容 |
|---|---|
version |
tsc --version,基本就是测启动速度 |
medium |
64 个文件,每个 24 个接口 + 常量,编译并输出 JS 和声明文件 |
declarations |
medium 再加一些条件类型、映射类型,只输出声明文件 |
huge-file |
单个文件,16384 个带类型标注的导出常量 |
所有测试都在同一台 Windows x64 台式机上完成。所有项目都是 Release + NativeAOT 编译的原生程序,C# 统一用工作站 GC。
测试日期:typescript-csharp (token) 是 2026 年 10 月 15 日,其余几个都是 10 月 20 日(终端截图也是 10 月 20 日重跑的)。这几个项目都还在快速迭代,你看到这篇文章时,数字很可能已经变了。
结果
先看相对 Go 的耗时倍数(Go = 1.00,越小越快):
| 项目 | 语言 | version |
medium |
declarations |
huge-file |
|---|---|---|---|---|---|
| ts-rust 0.7.0 | Rust | 0.82 | 0.77 | 0.75 | 0.97 |
| 官方 tsgo | Go | 1.00 | 1.00 | 1.00 | 1.00 |
| typescript-dotnet | C# | 1.11 | 1.29 | 1.30 | 1.86 |
| typescript-csharp (token) | C# | 1.07 | 2.05 | 2.39 | 2.31 |
| hez2010/TypeScript | C# | 1.14 | 3.63 | 2.98 | 3.91 |
再看绝对值(括号里是同一轮测试中 Go 的数据):
| 项目 | medium 耗时 |
huge-file 耗时 |
huge-file 峰值内存 |
|---|---|---|---|
| ts-rust | 45 ms(Go 58) | 261 ms(Go 270) | 156 MB(Go 166) |
| typescript-dotnet | 78 ms(Go 61) | 535 ms(Go 287) | 169 MB(Go 167) |
| typescript-csharp (token) | 138 ms(Go 67) | 683 ms(Go 296) | 178 MB(Go 159) |
| hez2010/TypeScript | 227 ms(Go 62) | 1134 ms(Go 290) | 241 MB(Go 172) |
写到这里,我又在终端里把 typescript-dotnet 对 Go 重跑了一次,下面是原样截图(输出里的 csharp 指的就是挑战者)。每次跑数字会有几个百分点的浮动,但倍数和上表基本一致:

一句话总结:Rust 比 Go 快,C# 比 Go 慢。C# 里最快的 typescript-dotnet,在中等项目上比 Go 慢三成左右,大文件上慢不到一倍;另外两个在中等项目上是 Go 的 2--4 倍耗时。

几点说明
为了不让大家在评论区替我说出来,先自己交代清楚:
- 这些都是很小的合成用例,几十毫秒级别。真实的大项目(比如 VS Code 的源码)我没测,结论不一定一样。比如 typescript-dotnet 作者自己测 VS Code 只慢 8.5% 左右,比上表好看不少。
- 只有 5 组样本,几十毫秒的用例本身噪音就有一二成,名次是稳的,小数点后第二位别太当真。
- 几个项目移植的上游 Go 版本并不完全相同,功能完整度也各不一样,这里只比较它们都能跑、并且输出一致的部分。
- 所有 C# 版本统一用工作站 GC。hez2010 的仓库里还测过 Satori GC,大文件能再快近两成,代价是内存更高。
- 如果你看过国庆那篇文章,可能记得"只慢 1.2--1.6 倍"这个数字。那是 hez2010 文档里另一组负载的结果;同一篇文章里引用的命令行端到端数据(64 模块 208 ms vs Go 55 ms,大文件 1100 ms vs Go 300 ms),和上表是一个量级。
- ts-rust 的 Windows 版用的是作者发布的二进制,其它几个是我本地编译的。
所以,C# 能超过 Go 和 Rust 吗?
我知道评论区会说什么:"C# 果然不行""慢了一倍还好意思发""早就说了 Anders 选得对"。
说得都对。
你想啊,Go 那边是 100 人年的家底,加上微软一支顶尖编译器团队一年多的移植;Rust 那边,Theo 前后烧了 42 万美元。C# 这边呢?三个项目,一个总共花了一百来美元,另外两个用的就是大家手里那种每月 100、200 美元的 Claude Code 或 Codex 订阅套餐。
拿一两百美元的订阅,去超过百万美元级别投入的成果------有可能吗?

而且这三个项目现在主要走完的,是"把行为对齐"这一步:先保证每一个报错、每一个字节都跟 Go 一样。性能优化,各家都才刚刚开始。typescript-dotnet 在检查器移植完后专门做了一轮,就从这些项目里跑到了最前面。
当然,ts-rust 的第一版也比 Go 慢两三倍,后来是让 AI 专门盯着性能跑了两周才反超的。不过 Rust 嘛,大家都知道,那是 Rust。
至于 C# 做完第二步、第三步之后会怎样------我也不知道。毕竟,这当然是不可能的。
想自己跑一下?
所有项目都是开源的,测试工具也在仓库里:
- 官方 Go 版:microsoft/typescript-go
- ts-rust:pingdotgg/ts-rust
- typescript-dotnet:stepwisehq/typescript-dotnet
- typescript-csharp:239573049/typescript-csharp
- hez2010/TypeScript:hez2010/TypeScript(
typescript-cs分支),测试工具在csharp/tools/performance-cli.mjs
原版工具只比较 Go 和 hez2010 自己的 C# 版。我在 sdcb/TypeScript 的 typescript-cs 分支上做了一个小改动:加了一个 --csharp-executable 参数,用来换成别的可执行文件(typescript-dotnet 的命令行参数不太一样,也做了个简单映射);另外加了 --ignore-input-activity,机器有人在用时也不丢弃样本。用 --go-executable 指定官方 Go 版,--csharp-executable 指定挑战者,就能得到和上面同样口径的成对数据:
powershell
node csharp/tools/performance-cli.mjs `
--manifest <C# 打包产物的 manifest.json> `
--go-executable <tsgo.exe> `
--csharp-executable <挑战者.exe> `
--samples 5 `
--filter '^(version|medium|declarations|huge-file)$'
如果你手上有一个更快的版本,或者在真实项目上测出了不一样的结果,欢迎在评论区贴出来。数据都在这里,欢迎打脸。

交流
感谢阅读到这里,如果感觉本文对您有帮助,请不吝评论 和点赞,这也是我持续创作的动力!
欢迎关注微信公众号 .NET骚操作,或者加入 .NET骚操作 微信交流群:

(如果微信群二维码过期,请加入 .NET骚操作交流QQ群 :495782587)