TypeScript 7 編譯器 (tsc) 的實驗性 Rust 移植版。
AI 翻譯
An experimental Rust port of the TypeScript 7 compiler (tsc)
開發工具Rust · JavaScriptGitHub @pingdotgg自行安裝營運者未確認
試用過了嗎? 點一下就好,不需要登入。
製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗I wanted to see if LLMs could port the TypeScript compiler, checker and lsp to Rust. Turns out they can.
It cost over $420,000 in tokens to do it, but you could probably have done it for ~$20k (see below).
Full type check of T3 Code with Effect diagnostics on.* Time is the sum of the medians for its five checked projects. Lower is faster. Bar length is in proportion to time. See Benchmark: T3 Code for the method and the run without Effect diagnostics.
| Checker | Time | Relative time | Speed vs tsc 7 |
|---|---|---|---|
tsc-rs --fast | 9.77s | █ | 4.35× |
tsc-rs | 14.30s | ██ | 2.98× |
tsc 7 | 42.55s | █████ | baseline |
bun check | 59.24s | ███████ | 0.72× |
tsc 6 | 191.00s | ████████████████████████ | 0.22× |
* tsc-rs is 0.6.0 with Effect diagnostics built in. tsc 7 is from @effect/tsgo. The
bun check time adds a separate effect-tsgo diagnostics run. tsc 6 uses the JavaScript
Effect plugin.
This is an early release. It has 100% compatibility in every real world project we have tested. It should work as a drop in replacement for the vast majority of apps. See Known problems.
Also worth mentioning: I've never read a line of this code.
Be warned, I have no idea if this will actually work.
npm install -D tsc-rs
npx tsc-rs -p tsconfig.json
I used a lot of OpenAI models to try and complete this port. In total I did over $400,000 in API priced tokens with GPT-5.6 Sol and GPT 6 Astra. They wrote over 1.3m lines of Rust over multiple months of /goal loops and never got past like 84% compat.
When I saw how little my Claude Code limits were burning, I figured it'd be fun to throw Opus 5.5 at this. It had a working v0 in 10 hours.
I assumed it kept using the code the Codex models wrote. I was wrong. Opus 5.5 started from scratch. It got further than Astra in 1/10th the time.
I let it keep going, and it definitely did. Total token spend was ~$24,047 of API spend over 2 weeks. I was using my Claude accounts, and it worked out to somewhere between 925% and 983% of my $200 plan weekly limits.
Expensive, for sure, but not that bad considering how much work has gone into typescript-go.
Everything below this was written by my LLMs, not me.
ts-rust is a direct port of Microsoft's native TypeScript compiler, which is written in Go
(microsoft/TypeScript, formerly
typescript-go). It keeps Go's algorithms and
behavior and has the same command line (tsc), language server and API.
npm install -D tsc-rs
npx tsc-rs -p tsconfig.json
tsc-rs takes the same options as tsc. The npm package is tsc-rs so that it does not clash
with the typescript package. To use tsc-rs as the typescript package (its tsc bin, its JS API
and the TypeScript 7 VS Code extension), install
@tsc-rs/typescript under that name (from the first release after
0.7.0):
{ "devDependencies": { "typescript": "npm:@tsc-rs/typescript@<version>" } }
Each release also has a standalone archive per
platform: the tsc binary with the lib files next to it.
Platforms: Linux x64 and arm64 (static, any distribution), macOS arm64, and (from the first release after 0.2.0) macOS x64, Windows x64 and Windows arm64.
To use it in VS Code, see @tsc-rs/typescript.
tsc-rs has the Effect language service diagnostics built in (codes
377xxx), so an Effect project needs no second compiler. They come from the same check as the
TypeScript diagnostics, and the language server shows them too. They run only when the tsconfig has
the plugin, as with @effect/language-service:
{ "compilerOptions": { "plugins": [{ "name": "@effect/language-service" }] } }
The rules, options and @effect-diagnostics comments are a port of
Effect-TS/tsgo 0.51.1. The language server also has its editor
features: the Effect hover (with the Layer summary), inlay hints, the auto-import style, quick fixes,
document symbols, completions and refactors. All the editor features of 0.51.1 are ported. The lightbulb,
the quick fix menu and the editor's Refactor menu show the Effect refactors: as in 0.51.1, the server lists
refactor.rewrite in its code action kinds.
The API stability rules of 0.51.1 read the @stability JSDoc tags of Effect 4.
unstableApiUsage (TS377136) and experimentalApiUsage (TS377135) report each use of an
unstable or experimental API, as warnings by default. allowedUnstableApis and
allowedExperimentalApis in the plugin entry allow a module (effect/cli) or one export
(effect/cli/Command#make). apiStabilityLeak (TS377137, off by default) is for library
maintainers. So a project on effect@4 gets TS377136 warnings for its unstable imports, as with
effect-tsgo 0.51.1. See the Effect diagnostics doc.
The port is pinned to one upstream revision, microsoft/TypeScript
3e9236361fa9
(2026-10-19, TypeScript 7.1.0-beta; UPSTREAM.md), and compared with Go at that
revision. To compare, use [email protected] (the same commit), not 7.0.x or
@typescript/native-preview. A difference that this build also shows is upstream behavior, and it
goes away when the port moves to a newer pin. tsc-rs 0.4.0 to 0.8.0 used the 2026-10-08 pin
([email protected]), and 0.3.0 and earlier the 2026-09-29 pin
([email protected]).
Four switches make some projects faster or use less memory. They are off by default.
--fast (or GOPORT_FAST=1) turns on the last two switches and more checkers. It runs one
checker per core, up to 16 and at most one per 32 source files, but never fewer than Go's 4
(--checkers N still sets the count, and tsc -b keeps 4). The cores are the ones that the
process can use (CPU affinity and cgroup limits), so N can be different on another machine.
Each file goes to the checker that Go's split gives it, so the output of --fast is the same on
every run and equals tsgo --checkers N for the chosen N, except where lazy members or the never
order change the work (see their bullets below). Where Go's output depends on N (zod, below),
the output of --fast can be different on another machine. --fast is an option of the
compiler command line only (as in Go, -b must come first). GOPORT_FAST=1 also turns on fast
mode in the language server (--lsp) and the API server (--api). On 16 vCPU Blacksmith
runners (tsc-rs 0.6.0, the benchmark below), VS Code checks in 2.64 s instead of 4.81 s,
Sentry in 2.97 s instead of 5.30 s and T3 Code apps/server in 2.78 s instead of 3.60 s.
Across the six apps below, --fast is 2.90x the speed of tsc 7 against 2.10x for the
default mode (geometric mean). The gain is small or zero on small projects. CPU time went up
as much as 2.2x and peak memory as much as 1.9x in our 16-CPU measurements. On all 16
benchmark cases below, the diagnostics were the same as without --fast. On zod, TS2344 and
TS5115 errors in a type cycle can appear or go away, as in Go with more checkers.
GOPORT_FAST_STEAL=1 (with --fast) also turns on work stealing: a checker that has checked
its own files takes unchecked files from the checker with the most work left. It can cut wall
time further on large projects and can cost time on small ones. A file can then go to a
different checker in each run. So where Go's output depends on which files share a checker,
the output can change from run to run (on zod it does).
--checkers 8 (Go's option; the default is 4) runs 8 checkers. On an 8-core host, VS Code
checks in 30% less time and mui in 23% less, but T3 Code server gains nothing. CPU time goes
up 13% to 51% and peak memory 7% to 34%. The output is the same as Go's with --checkers 8.
Go's own output can change from 4 to 8 checkers (on zod, a TS5115 moves).
GOPORT_LAZY_MEMBERS=1 makes a member of an instantiated class or interface only when the
checker needs it, as microsoft/TypeScript#64475 does (open, not in the pin). Peak memory goes
down (zod 15%, T3 Code server 6.5%, Effect 4%). The time stays about the same. The
Symbols count is then lower than Go's. Diagnostics and emit stay the
same as Go's, except when the members of a type resolve inside their own resolution (a cycle):
there, as with #64475 in Go, a TS2589, TS5114 or TS5115 can move to another node or go away.
None of the tested projects has such a case.
Results of the last accepted revision, R222 (2026-10-22), against Go at the pin 3e9236361fa9
([email protected]). Every revision runs these checks on our own machines
(Blacksmith testboxes and a LAN host), not in GitHub CI. We accept it only with no lost result.
| Check | Compared with Go | Result |
|---|---|---|
| Compiler test corpus | 13,728 configurations of 12,932 compiler and conformance cases | Diagnostics 13,728 of 13,728 and emit 13,654 of 13,654 match byte for byte |
| Test suite | Go's tests and baselines (ported), and our own tests | 186,890 pass, 0 fail |
| np-suite | API client tests of packages/typescript | 966 of 966 pass |
| Language server | 257,903 requests in 6 batteries | 243,771 match, 0 differ |
| API | 112,740 requests in 13 batteries | 112,681 match, 4 differ |
| Gate | tsc-rs and tsgo on corpus samples, real projects, build mode and editor sessions | 3,951 of 3,952 match, 1 allowed |
| Query core and Hono | Full check (2 runs) and emit | Same as Go (Hono has 2 TS7031 errors in both) |
testdata/tests/cases/compiler and conformance, except what Go
skips too (44 files, 1,720 configurations, 8 emit checks). Types, symbols and source maps match.tsc-rs makes, not in the
answers. The allowed gate item is a resolution trace whose order changes between threaded Go runs.Full type check of T3 Code at
cd41c4ad,
with and without Effect diagnostics. Each time is the sum of the medians for apps/server,
apps/web, apps/mobile, packages/client-runtime and packages/shared, not the elapsed time
of the parallel benchmark workflow. Lower is faster.
With Effect diagnostics
Bar length is in proportion to time.
| Checker | Time | Relative time | Speed vs tsc 7 |
|---|---|---|---|
tsc-rs --fast | 9.77s | █ | 4.35× |
tsc-rs | 14.30s | ██ | 2.98× |
tsc 7 | 42.55s | █████ | baseline |
bun check | 59.24s | ███████ | 0.72× |
tsc 6 | 191.00s | ████████████████████████ | 0.22× |
With Effect, tsc-rs --fast is fastest: 4.35× the Effect-patched tsc 7 and 6.1× Bun
followed by the separate Effect pass. On the smallest projects (apps/mobile,
packages/shared) --fast gains little or nothing.
Bar length is in proportion to time.
| Checker | Time | Relative time | Speed vs tsc 7 |
|---|---|---|---|
bun check | 6.24s | ██ | 3.12× |
tsc-rs --fast | 6.91s | ██ | 2.81× |
tsc-rs | 7.95s | ██ | 2.45× |
tsc 7 | 19.45s | █████ | baseline |
tsc 6 | 87.87s | ████████████████████████ | 0.22× |
tsc-rs is 0.6.0. Bun is fastest without Effect.
Diagnostics matter when comparing these times:
tsc-rs and tsc-rs --fast match the pinned tsc 7 diagnostics exactly.
Both Rust modes and tsc 6 report the same TS2322 in the server fixture. Bun reports no
TypeScript errors.tsc-rs, tsc-rs --fast and the native Effect compiler agree on
4,823 Effect diagnostics, including 4,602 unstable-API warnings. The separate Effect
pass after Bun reports the same rules and messages, with different path and severity
formatting.tsc-rs additionally
reports the TS2322 above, which the pinned tsc 7 also reports; the Effect compiler's older
TypeScript version does not. The JavaScript Effect plugin uses a different rule set and
reports 305 diagnostics in total. These tools do not all perform identical checks.Measured on 2026-10-13 UTC, on Blacksmith blacksmith-16vcpu-ubuntu-2404 runners
(16 AMD EPYC vCPUs, about 62 GiB RAM). Each project/mode has its own runner; all five checkers
for that case run on that same runner. Every cell has 5 runs after 1 warmup, with
--noEmit --incremental false --composite false and default thread counts. TypeScript 6
runs on Node 24.19.0 with a 16 GiB heap.
Without Effect, the configs have no Effect plugin and tsc 7 is 7.1.0-dev.20261008.1 (the
pin of 0.6.0). With Effect, @effect/tsgo 0.51.1 supplies its TypeScript 7.0.2 executable; the
JavaScript comparison uses TypeScript 6.0.3 and @effect/language-service 0.87.4. Bun is
1.4.4-canary.1+c69a636b9. tsc-rs is 0.6.0: a release PGO build without BOLT of the tag
source (a8c5b7063), the same build
process as the npm packages. tsc-rs --fast is the same binary with --fast.
Results, samples, hashes and per-case metadata come from this Blacksmith run, which also measured the app benchmark below. The workflow caches the verified compiler and prepared inputs before running the comparisons in parallel. The T3 Code runner is scripts/bench-apps/t3code.sh.
Full type check of six open-source apps on Blacksmith blacksmith-16vcpu-ubuntu-2404
runners (16 AMD EPYC vCPUs, about 62 GiB RAM). Each app has its own runner; all five checkers
for an app ran on that runner. Lower times are faster.
| App | tsc 6 | tsc 7 | tsc-rs 0.6.0 | tsc-rs 0.6.0 --fast | bun check |
|---|---|---|---|---|---|
| VS Code | 91.75s | 9.57s | 4.81s | 2.64s | 2.32s |
| Sentry (frontend) | 80.27s | 13.49s | 5.30s | 2.97s | 6.93s |
| Playwright | 6.33s | 0.82s | 0.41s | 0.33s | 0.27s |
| Excalidraw | 6.85s | 1.42s | 0.62s | 0.48s | 0.24s |
| TypeORM | 5.60s | 0.70s | 0.39s | 0.27s | 0.15s |
| tRPC (server package) | 1.55s | 0.17s | 0.09s | 0.09s | 0.10s |
tsc-rs 0.6.0 is 2.10× faster than tsc 7 across these apps (geometric mean).
The corresponding figures are 2.90× for tsc-rs --fast and 3.29× for Bun. Bun is fastest
on four apps; tsc-rs --fast is fastest on Sentry and tsc-rs on tRPC, where --fast
gains nothing (0.088 s against 0.085 s).
Diagnostics differ between compilers. tsc-rs, tsc-rs --fast and the pinned tsc 7
produce identical full diagnostics: 10 on VS Code, 2 on Sentry, and none on the other four
apps. tsc 6 reports 9 on VS Code and none on the others. Bun reports none on any app.
The time comparisons do not imply identical checking behavior for Bun or tsc 6.
Measured on 2026-10-13: median of 5 runs after 1 warmup, --noEmit --incremental false,
default thread counts, and direct native binaries. tsc 6 runs on Node 24.19.0 with a 16 GiB
heap. Versions: TypeScript 6.0.3 and 7.1.0-dev.20261008.1 (the pin of 0.6.0), tsc-rs 0.6.0,
and Bun 1.4.4-canary.1+c69a636b9. The tsc-rs build is release PGO without BOLT, static
musl, built from the tag source with the same training process as the release workflow.
Results, samples, hashes and input revisions come from this Blacksmith run, the same run as the T3 Code benchmark above. Every job passed.
The pinned app inputs use the same preparation as the earlier benchmark:
baseUrl, which native TypeScript no longer supports.nodenext module resolution.The scripts are in scripts/bench-apps. The benchmark workflow caches the verified compiler and each prepared project, then runs each comparison on a separate runner of the same size. Each comparison keeps all compilers on one runner and saves their full output and exit codes.
bun check is fasterThe lead of bun check is all in the check step. Its checkers share one type store on all cores.
tsc 7 runs 4 checkers, and each one makes its own copies of the types it needs: on T3 Code
apps/server, 57% of the types. tsc-rs keeps the tsc 7 model, because its goal is output that
is identical to tsc 7. More checkers or one shared store would change that output, so tsc-rs
does not copy them. --checkers 8 uses more cores with output identical to tsc 7 at
--checkers 8 (see Status).
Per thread, tsc-rs is not slower. On T3 Code it was 1.5× to 7× faster per thread than
bun check, and on 28 other open-source repos the two did about the same check work per thread.
tsc-rs also loads and parses files as fast as bun check or faster.
The output is not the same. bun check gave output identical to tsc 7 on 37 of 60 open-source
repos, and tsc-rs on all 60. bun check also reports errors that tsc 7 does not, for example
TS2749 on T3 Code apps/server. These numbers are from our studies of 2026-10-07 and 2026-10-08,
with tsc-rs 0.1.0 and R179 on Linux.
node_modules and through a direct import. There, tsc-rs can write output for more of those
files than tsc does, and report TS6059 (file is not under rootDir) for them. tsc decides
this by timing, so its own result changes between runs. tsc-rs gives the same result in every
run (the result of TypeScript 6).tsc -b, when one project imports the output of another project without a project reference,
the two projects can build at the same time. Then tsc reads each output file before or after the
other project writes it. This is timing, so the result of tsc changes between runs, file by
file. tsc-rs makes the programs one at a time and writes the outputs of a project together when
that project ends. So it can read old or missing output where tsc reads new output, or the
reverse. Often the only difference is the file hashes in the reading project's .tsbuildinfo.
Sometimes it is an error, such as TS2305, TS2307 or TS2322. Add the reference to fix it.tsc grows 2.6 to 2.8. It starts within 10% of tsc's, and from
about edit 20 it stays below tsc's. After 10,000 edits (two sessions each) it was 232 and 249
MiB against tsc's 291 and 305 MiB on Query core, and 314 and 324 MiB against 417 and 422 MiB on
Hono.tsc-rs --version prints the TypeScript version that it ports (7.1.0-beta), not the npm
version. The compiler matches typesVersions against it.crates/ts_goport is the compiler. It has three parts crates in crates/ts_goport/parts:
goport_core (everything below the language service), goport_util and goport_lsproto. Their
files stay in crates/ts_goport/src. It uses the lib files in crates/ts_goport/libs.
tools/ts_ast_codegen generates crates/ts_goport/src/astdata, and
tools/ts_diagnostics_codegen generates crates/ts_goport/src/diagnostics/catalog.rs and
crates/ts_goport/src/diag.rs. crates/ts_wasm is the WebAssembly build
(npm/wasm).
./scripts/run-cargo-capped.sh build --release -p ts_goport --bins
./scripts/verify.sh
The bins are goport (type check) and tsgo (the Go tsgo command line). The Go baseline tests
run with
TS_GO_REPO=/path/to/typescript-go ./scripts/run-cargo-capped.sh test -p ts_goport --test go_baselines.
CI builds and tests x86_64-pc-windows-msvc (the Windows job of
ci.yml).
git clone -c core.autocrlf=false .... .gitattributes forces LF
for *.rs, *.sh and the bundled libs (the lib snapshot tests hash their bytes), but the test
fixtures need LF too.cargo directly. scripts/run-cargo-capped.sh needs systemd-run.RUST_TEST_THREADS=1, as CI and goport-tests.sh do. The Go baseline tests
need TS_GO_REPO and Node, as on Linux (see the CI job for the setup).fswatch_watcher tests (test_subscribe_symlink_create and _delete) make file symlinks,
which need Developer Mode (Settings, System, For developers) or an elevated shell. Without
either they fail with OS error 1314.crates/ts_goport/scripts/build-pgo.sh,
copy-libs.sh, scripts/goport/pgo-train.sh) and scripts/goport/win-go-compare.sh run in
Git Bash. The other tools in scripts/ (the revision pipeline, the gate, remote.sh) are for
the Linux hosts only.Push a tag v<version> (for example v0.1.0). The
release workflow builds, packs and tests the packages, publishes
them to npm and creates a GitHub release. A stable version goes to the dist-tag latest, and a
prerelease version (v0.2.0-beta.1) to next and a GitHub prerelease. See
npm/README.md.
MIT. The port keeps the licenses and notices of the code it ports: TypeScript (Apache-2.0) and parts of the Go standard library (BSD-3-Clause). See NOTICE.md.
--extendedDiagnosticsGOPORT_FAST_NEVER_ORDER=1 checks some intersection types in a faster order (mongodb's
test project: 8.9 s to 1.6 s). The --extendedDiagnostics counts are then lower than Go's.
Diagnostics and emit stay the same as Go's, except near the TS2589 limit (5,000,000 type
instantiations): there, a TS2589 that Go reports can go away.
Geometric mean vs tsc 6 | 1.0× | 7.3× | 15.3× | 21.2× | 24.0× |