2026年4月21日、Microsoft が TypeScript 7.0 Beta を公開した。見出しに踊る「10倍速」「Go 言語に移植」という言葉だけでは、実プロジェクトに何が起きるのかは見えてこない。
この記事では、Microsoft 公式発表と、筆者が実際に tsgo を触った所感をもとに、
--checkers / --builders / --singleThreaded フラグの使い分けpackage.json の書き方を順番に、なるべく具体的に書いていく。「@katsujin」
TypeScript はバージョン 1 のころから、TypeScript 自身で書かれた TypeScript コンパイラを Node.js 上で動かす、というブートストラップ構造を取ってきた。これは言語の自己ホスティングとして美しく、また「言語チーム自身が言語のドッグフードを食べる」という意味でも意味があった。
ただ、その美しさには代償があった。
この構造のまま、TypeScript は数百万行規模のモノレポを抱える Bloomberg、Figma、Google、Notion、Slack、Vercel といった企業の本番現場へと侵食していった。コードベースが大きくなるほど、tsc の起動・型チェック・エミットにかかる時間は線形以上に伸び、CI の待ち時間と開発者の集中力を奪っていった。
TypeScript 7.0 は、この前提そのものを書き換える。
Built on a new native and parallelized foundation, it's already being used on multi-million line codebases.
— TypeScript チーム
Microsoft はこの 1 年強をかけて、既存の TypeScript コードベースを Go 言語に移植した。「ゼロから書き直した」のではなく、「機械的に移植した」点が重要だ。Daniel Rosenwasser はブログでこう書いている。
The new Go codebase was methodically ported from our existing implementation rather than rewritten from scratch, and its type-checking logic is structurally identical to TypeScript 6.0.
つまり、型チェックのセマンティクスは 6.0 と構造的に同一。10 年かけて積み上げたテストスイートをパスしているし、すでに Microsoft 社内外の数百万行コードベースで実戦投入されている。"Beta" のラベルに怯む必要はない、というのが彼らのメッセージだ。
公開直後から各所で繰り返された質問がこれだ。コメント欄でもまさにそのやり取りがある。
curious: why go vs rust?
リードアーキテクトの Anders Hejlsberg(C#、Turbo Pascal、Delphi の生みの親でもある)が語った選定理由は、要約するとこうなる。
ここで効いてくるのは、TypeScript コンパイラが型グラフという巨大なポインタの森を扱うソフトウェアだということだ。型ノードが相互に参照し合い、生成と破棄が頻繁に起きる。Rust の所有権モデルでこの構造を素直に書こうとすると、Rc<RefCell<_>> の海になるか、アリーナアロケータで手動管理するかのどちらかを迫られる。既存の JavaScript/TypeScript コードを「機械的に移植する」というプロジェクト目標に対して、これは大きな摩擦になる。
一方 Go なら、ポインタと GC で素直に書ける。クラス(構造体+メソッド)も書ける。並行処理は goroutine で書ける。TypeScript の AST やシンボルテーブルの構造をほぼそのままの形で持ち込める。
コメント欄の Simon Langlois がうまくまとめている。
Microsoft's decision to port the TypeScript compiler to Go instead of Rust was driven by the need for a pragmatic, high-speed "port" rather than a multi-year "rewrite".
「数年かけたリライト」ではなく「実用的な高速移植」を選んだ ― これが Go だった理由だ。
「ネイティブバイナリ化」だけで 10 倍にはならない。Node.js + V8 はそんなに遅くない。 10 倍の本丸は共有メモリ並列化にある。TypeScript 7.0 は、以下の 3 つのフェーズを並列に動かす。
| フェーズ | 並列化のしやすさ | 7.0 での扱い |
|---|---|---|
| Parsing(構文解析) | ファイル単位で完全独立 | ほぼフル並列 |
| Type-checking(型検査) | ファイル間で型情報を共有する必要あり | 限定的並列(後述) |
| Emit(JS 出力) | ファイル単位で独立 | ほぼフル並列 |
問題は型チェックだ。型チェックは
ため、ナイーブに「ファイルを N 個のワーカーにばらまく」と、ワーカーごとに同じ型情報を再計算してメモリと CPU を浪費するし、最悪は結果がブレる。
TypeScript 7.0 のチェッカーはこの問題に対し、固定数の type-checker worker を立てて、それぞれに「自分専用のワールドビュー」を持たせるアプローチを取った。
これにより「並列化したのに毎回違うエラーが出る」という最悪パターンを避けつつ、コア数のスケーリングを得ている。
このアーキテクチャを露出させるための新しいコマンドラインフラグが 3 つ追加されている。
tsgo --checkers 4 --builders 4 --singleThreaded
--checkers <N>(デフォルト 4) 型チェッカーワーカーの数。コア数が多いマシン(ローカル開発機など)では増やすと速くなる。逆に CI ランナーのような貧弱な環境では減らした方がオーバーヘッドが少ない。ただし、稀にこの数値を変えると順序依存の結果差異が出る。チーム内で固定値を決めておくのが無難。
--builders <N>(プロジェクト参照ビルダーの並列数) モノレポで複数の tsconfig.json を持つプロジェクト参照ビルドを、複数並列に走らせる。--checkers と乗算的に効くので注意。たとえば --checkers 4 --builders 4 は最大 16 ワーカーが同時に走り、メモリを食い尽くす可能性がある。
--singleThreaded すべて単一スレッドで動かす。デバッグ、TS 6 との性能比較、外部からビルドを並列オーケストレーションしているケース、リソース極小環境で使う。
実運用での落とし所としては、
--checkers 8 --builders 2 あたりから試す--checkers 2 --builders 1、メモリ次第で --singleThreaded--builders を上げる前に依存グラフのボトルネックを確認を最初のチューニングポイントにすると良さそうだ。なお、--builders は値を変えても結果は変わらない(順序非依存)と公式が明言している点も覚えておきたい。
ここが移行を考える上でいちばん実務的なパートだ。
npm install -D @typescript/native-preview@beta
そして tsc の代わりに tsgo を叩く。
> npx tsgo --version
Version 7.0.0-beta
挙動は tsc と互換。ただし速い、それだけ。
エディタ側は TypeScript Native Preview 拡張機能 for VS Code を入れる。LSP(Language Server Protocol)の上に作られているので、VS Code 以外のモダンエディタや、Copilot CLI のようなツールでも動く。
ここが地味に重要。
TypeScript 7 の Beta は tsgo という別名で動くので衝突しないが、安定版になると typescript パッケージ名・tsc 実行ファイル名を取り戻す。一方、typescript-eslint を始めとする多数の周辺ツールは、typescript パッケージから直接 import するピア依存を持っている。周辺ツールがまだ TS7 に未対応の段階で、tsc だけ先に 7 にしたいという需要が必ず出る。
そのための互換パッケージとして @typescript/typescript6 が公開されている。これは
tsc6 という別名のエントリポイントを提供する。つまり「tsc は TS7、ライブラリが import する typescript API は TS6」という分離が可能になる。
具体的には npm エイリアスを使ってこう書く。
{
"devDependencies": {
"typescript": "npm:@typescript/typescript6@^6.0.0"
}
}
または、
npm install -D typescript@npm:@typescript/typescript6
これで import "typescript" する周辺ツールは TS6 の API を見続けながら、CLI の tsc6 で TS6 ビルドを、tsgo(将来は tsc)で TS7 ビルドを、それぞれ走らせられる。Beta 期間中はこの構成で両方の結果を CI で比較するのが現実的な検証スタイルだ。
なお Microsoft は「安定版のプログラマブル API は早くて TS 7.1」と明言している。コンパイラ API を直接叩いてカスタムツールを作っているチーム(AST 変換、独自リンター、Language Service プラグインなど)は、しばらく typescript-eslint 等と同じく外側から tsgo を叩く運用に切り替えるか、TS6 API のまま留まる判断になる。
公式は「TS6 でクリーンに通るコードは TS7 でも同一に通る」と書いているが、これは 6.0 の新しいデフォルトをすべて受け入れていればという前提付きだ。5.x からいきなり 7.0 を試すと、相当数の設定が爆発する。
| オプション | 新デフォルト | 影響 |
|---|---|---|
strict | true | 暗黙的 any、null チェック、関数型の厳格化が全部 ON |
module | esnext | ESM 前提。CommonJS 系のプロジェクトは設定必須 |
target | esnext 直前の安定版 | ES5 ターゲットは死亡 |
noUncheckedSideEffectImports | true | 副作用 import のチェック厳格化 |
libReplacement | false | @typescript/lib-* パッケージによる lib 置換が効かなくなる |
stableTypeOrdering | true(固定) | 型推論の安定化、もう off にできない |
rootDir | ./ | これは要注意(後述) |
types | [] | これも要注意(後述) |
rootDir の罠:src/ 配下にソースを置いて tsconfig.json をルートに置く構成だと、rootDir のデフォルト ./ のせいで出力ディレクトリ構造が変わってしまう。明示が必要。
{
"compilerOptions": {
"rootDir": "./src"
},
"include": ["./src"]
}
types の罠:これまで暗黙的に node_modules/@types/* が全部読み込まれていたのが、明示リストにしか反応しなくなる。@types/node や @types/jest に依存していて気づかない人が多そうな箇所。
{
"compilerOptions": {
"types": ["node", "jest"]
}
}
旧挙動に戻すなら "types": ["*"]。
ここからが本番。「もう動きません」になったものたち。
target: es5 ― ES5 ターゲットは廃止。IE はとっくに死んでるよねという話downlevelIteration ― 同上moduleResolution: node / node10 ― 代わりに nodenext / bundlermoduleResolution: classic ― 同上module: amd / umd / systemjs / none ― 代わりに esnext か preserve + バンドラbaseUrl ― 廃止。paths をプロジェクトルート相対で書き直すesModuleInterop / allowSyntheticDefaultImports ― false に設定不可(=常に true)alwaysStrict ― 常に true、false 不可namespace 宣言内の module キーワード ― 不可asserts キーワード ― ECMAScript の import attributes に合わせて with に統一/// <reference no-default-lib /> ― skipDefaultLibCheck 下では尊重されないtsconfig.json があるときの tsc <file> ― --ignoreConfig を明示しない限り不可このリストを眺めると、「TypeScript で書かれた古いビルド設定」がそのまま 7.0 で動く可能性はかなり低いことがわかる。とくに baseUrl を使ったパスエイリアスは多くのプロジェクトで使われているし、module: commonjs でやっている Node.js プロジェクトはモジュール解決の見直しが必須になる。
公式が推奨しているのは「まず TS6 に上げて、6 の上で新しいデフォルトと向き合ってから 7 に行く」という二段ロケット。一足飛びに 5.x → 7.0 を試みると、コンパイラの 10 倍速さに辿り着く前に設定エラーで何時間も溶かす。
意外と見落とされそうなのが、.js ファイルに対する型解析の再設計だ。
これまで TypeScript は、JS ファイルに対しては「JSDoc コメント+人気のコーディングパターン」を組み合わせて型を推論していた。Closure Compiler 風の @class や @enum、function(string): void 形式の関数型、this の別名付け、prototype 全置換などの「ふんわりした JS 慣習」を特別扱いしていた。
TypeScript 7.0 では、この特別扱いをやめる方向に振り切った。.ts での解析と一貫させる。具体的には:
| Before(TS6 まで) | After(TS7) |
|---|---|
@param {someVar} x のように値を型として使える | typeof someVar を明示する |
@enum を特別解釈 | (typeof YourEnum)[keyof typeof YourEnum] パターンを使う |
単独 ? を型として使える | any を使う |
@class で関数をコンストラクタ扱い | class 宣言を使う |
後置 ! で non-null 表明 | T のまま書く |
/** @typedef {T} */ Name; の隣接記法 | /** @typedef {T} Name */ 形式に統一 |
function(string): void の Closure 風記法 | (s: string) => void に統一 |
this 別名・prototype 全置換の特別扱い | 廃止 |
JSDoc で頑張ってきたレガシー JS 資産は、動くけれど型がつかなくなるという症状が出始める可能性が高い。差分の詳細は microsoft/typescript-go の CHANGES.md で追跡されている。
TypeScript Native Preview 拡張機能は、コマンドラインと同じネイティブバイナリを Language Server として動かす。これが効くのは、
といった、文章では伝わりにくいが体感では明らかな部分だ。Daniel Rosenwasser も「a much more lightweight and fluid editing experience」と書いている。
機能面では、Beta 時点で
が揃っており、まだ来ていないのは「セマンティクス強化シンタックスハイライト」「より細かい import 管理コマンド」といった枝葉だけ。日常使いの満足度は十分高い、と評されている。
LSP ベースなので、Neovim、Helix、Zed、Cursor、そして Copilot CLI などからも同じ体験が得られる。「IDE のためにバイナリを別個に用意する」発想を捨てて、「LSP に統一して全エディタ共通で速くする」という設計判断は、地味だが今後数年効いてくる。
ここまで読んで、「で、結局月曜から何をすればいいの」となったあなたに、優先順位順に。
① まず開発機の VS Code に Native Preview 拡張を入れる 今のプロジェクトに何の影響も与えずに、エディタ体験だけを試せる。リスクほぼゼロ。これが一番ハードルが低い検証で、しかも体感差がいちばん大きい。
② CI に並走ジョブを追加する 既存の tsc ジョブはそのままで、tsgo --noEmit だけ走らせる並走ジョブを追加する。@typescript/native-preview@beta を devDependency に足すだけで済む。差分が出たら issue tracker に投げる。これで自プロジェクトの 6→7 互換性が見える。
# .github/workflows/typecheck.yml (抜粋)
- run: npx tsc --noEmit # 既存
- run: npx tsgo --noEmit # 追加(失敗しても warning 扱い)
continue-on-error: true
③ tsconfig.json を 6.0 互換へ寄せておく これは時間がかかる作業なので早めに。とくに
baseUrl を paths +プロジェクトルート相対に置換types を明示リスト化moduleResolution を nodenext か bundler にmodule: commonjs のプロジェクトはバンドラ前提か Node ESM 前提かを意思決定このあたりは、7.0 が GA する前に終わらせておきたい宿題だ。
公式が示しているスケジュールは以下。
--watch の効率化、JS ファイルからの宣言ファイル emit の同等性確保つまり安定版まで、おそらくあと 2 ヶ月以内。typescript-eslint などの周辺エコシステムも、安定版を待ってから本気の対応に乗り出すはずだ。
TypeScript が自分自身で書かれていることは、コミュニティにとってある種のアイデンティティだった。「私たちの言語は、私たちの言語で動く」。それを Microsoft は捨てた。
捨てた理由はシンプルで、TypeScript はもう「JavaScript の少し型がついた版」ではないからだ。Bloomberg、Figma、Google、Notion、Slack、Vercel ― この規模のコードベースで動く言語処理系には、JavaScript ランタイムの天井よりも、ネイティブコード速度と並列性のほうがはるかに価値がある。
そして注目すべきは、書き直さずに移植したという選択。型チェッカーのロジックを構造的に同一に保ち、10 年分のテストスイートを担保にして、「速くなった以外、何も変わらない」状態を目指した。これは派手な発表の裏で、極めて保守的で実務的な意思決定だ。
10 倍速は確かに見出しに使える数字だが、私が個人的にいちばん大きいと思っているのは、LSP に統一されたエディタ体験のほうだ。Beta を 1 日触ってみて、補完が出るまでの「あの 200ms」が消える感覚は、CI ビルド時間の短縮よりも日々の集中力に効く。
npm install -D @typescript/native-preview@beta で、明日から試せる。 試して、Issue を投げよう。あと 2 ヶ月で、tsc はもう Go バイナリだ。