技術約7分で読めます

TypeScript 7の10倍速は型が重いコードでも出るのか、drizzle-orm・Effect・lobe-chatをM4 Mac miniで実測

いけさん目次

TypeScript 7が正式リリースされた。コンパイラと言語サーバーを丸ごとGoに移植したネイティブ版で、公称は「フルビルドで8〜12倍」。
リリース当日にM4 Mac mini(10コア/16GB)へ入れて、公式ベンチマークに出てこないタイプのコードベースで実測した。

実測ではファイル数が多く型がシンプルなlobe-chatで7.3倍が出た一方、型レベルの計算が重いEffectは5.3倍、drizzle-ormは3.9倍で、公称の下限に及ばなかった。
16GBのマシンだと、公式が「もっと速くなる」と紹介している--checkers 8はメモリを圧迫して逆に遅くなる。
Vue製のComfyUI_frontendではそもそも動かなかった。

TypeScript 7の変更点

2025年3月に発表されたGo移植が正式版になった。

項目内容
ネイティブ化Go実装+共有メモリのマルチスレッド。フルビルドで8〜12倍が公称値
エディタ対応LSP(言語サーバープロトコル)を正式サポート。VS Codeは専用拡張で、数週間以内に本体同梱予定
並列制御--checkers(型チェックワーカー数、既定4)、--builders(プロジェクト参照の並列ビルド)、--singleThreaded
watchモードParcelのファイルウォッチャーをC++からGoに移植した新実装
APIなし。7.1で新設計のAPIを出す予定。つなぎに@typescript/typescript6パッケージ(tsc6バイナリと6.0 API)が用意された
互換性型チェック結果は6.0互換。6.0で非推奨になったフラグはハードエラーに変わる

ネイティブ版になってもnpmのパッケージ名はtypescriptのままで、npm install -D typescript@7で入る。
中身はesbuildと同じ方式で、@typescript/typescript-darwin-arm64のようなプラットフォーム別パッケージにネイティブバイナリが入り、bin/tscは薄いNodeラッパーになっている。

6.0の破壊的変更(strictのデフォルト化、baseUrl廃止など)はTypeScript 6.0 RCの記事に書いた。7.0ではそれらがハードエラーになる。

typescript-eslintのようにAPIを使うツールと併用する場合は、npmエイリアスで6.0系をtypescript名義に残す構成が公式から出ている。

{
  "devDependencies": {
    "typescript": "npm:@typescript/typescript6@^6.0.2",
    "typescript-7": "npm:typescript@^7.0.2"
  }
}

計測対象の選定

公式ブログのベンチマークはvscode(125.7秒→10.6秒で11.9倍)、sentry、bluesky、playwright、tldrawの5つ。いずれもファイル数が多いアプリケーションコードで、倍率は7.7〜11.9倍だった。

パースと出力(emit)はファイルごとにほぼ独立に処理でき、型チェックも既定で4つのワーカーに分かれて走る。
逆に、1ファイルの中で型のインスタンス化が深く連鎖するコードは分割しようがない。スキーマ定義から型を導出するORMや、型レベルプログラミングが中心のライブラリは、公式の表に入っていない。

そこで計測対象をこの4つにした。

リポジトリ規模ピン留めTS型チェックの性質
drizzle-orm(本体パッケージ)448ファイル・6.2万行5.6.3スキーマからの型導出が支配的
Effect(モノレポ全体)1,803ファイル・58.8万行5.8.3型レベルプログラミングが中心。tsc -bで35パッケージ
lobe-chat8,451ファイル・118万行6.0.3型がシンプルな大規模アプリ。比較用の基準
ComfyUI_frontend2,694ファイル・52.3万行5.9.3(vue-tsc)Vue SFC込み

lobe-chatとComfyUI_frontendは、生成AI系でおなじみのコードベースから選んだ。
lobe-chatのtype-checkスクリプトはすでにtsgo --noEmitで、ネイティブプレビュー版を本採用済みだった。今回の正式版はその安定版に当たる。

検証環境

項目内容
マシンM4 Mac mini(10コア / 16GB)
Node25.3
TypeScript 77.0.2(@typescript/typescript-darwin-arm64のネイティブバイナリを直接実行)
比較対象各リポジトリがピン留めしているTypeScript(tsc.jsをNodeで実行)
計測/usr/bin/time -lで実時間とピークメモリ(最大RSS)を取得、各3回の中央値

比較側をピン留め版にしたのは、「そのリポジトリの開発者が日常見ている速度」との差を出すため。
Effectは毎回tsc -b --cleanでフルビルドに戻し、TypeScript 7側は断りがなければ既定の--checkers 4で実行した。

既存のtsconfigのままでは走らない

3つとも、既存のtsconfigのままTypeScript 7を当てると設定エラーで止まった。

drizzle-ormではbaseUrlがエラーになる。

tsconfig.json(4,3): error TS5102: Option 'baseUrl' has been removed. Please remove it from your configuration.
  Use '"paths": {"*": ["./*"]}' instead.
tsconfig.json(6,12): error TS5090: Non-relative paths are not allowed. Did you forget a leading './'?

対応は2箇所で、baseUrlの行を消し、paths./始まりの"~/*": ["./src/*"]に書き換える(TS5090が./を要求する)。これで通った。

EffectはdownlevelIterationesModuleInterop: falseが拒否された。実体はtsconfig.base.jsonの2行なので削除は一瞬で終わる。
フラグの削除とは別に、5.8.3から7.0に飛ぶと同梱のlib.d.tsも数世代新しくなる。EffectではUint8ArrayとArrayBufferまわりの型定義変更に引っかかり、9ファイルで34件の型エラーが出た。

packages/platform-node/src/internal/httpIncomingMessage.ts(83,5): error TS2375:
  Type 'Effect<Uint8Array<ArrayBufferLike>, E, never>' is not assignable to
  type 'Effect<ArrayBuffer, E, never>' with 'exactOptionalPropertyTypes: true'.

コンパイラ本体の互換性は6.0基準で作り込まれている一方、5.x系のままのdrizzle-ormとEffectでは、速度を測る前にこの設定エラーと型エラーの対処が必要だった。

結果

フルチェックの実時間(3回の中央値)。

リポジトリピン留めTSTypeScript 7倍率
drizzle-orm3.70秒(5.6.3)0.94秒3.9倍
Effect(tsc -b)42.5秒(5.8.3)8.1秒5.3倍
lobe-chat112.7秒(6.0.3)15.4秒7.3倍

ピークメモリ(最大RSS)。

リポジトリピン留めTSTypeScript 7
drizzle-orm1.1GB1.5GB
Effect3.1GB1.9GB
lobe-chat7.9GB9.5GB

lobe-chatのTypeScript 6は素の状態だとNodeがクラッシュし、NODE_OPTIONS=--max-old-space-size=12288でヒープを広げてようやく完走した。TypeScript 7はネイティブプロセスなので、この種のヒープ調整自体が不要になる。
一方でピークメモリはlobe-chatだと9.5GBまで伸びた。公式が言う「メモリ削減」はビルド全体で確保する総量の話で、並列ワーカーが同時に使うピークはむしろ増えた。

Effectは--builders 8を足すと8.1秒が7.1秒(6.0倍)になった。依存グラフの中でまだ並列にビルドできるパッケージが残っていた分の差だ。

—checkersを振った結果

--checkersを1、4(既定)、8で振った。

checkers 1checkers 4checkers 8
drizzle-orm1.24秒 / 0.8GB0.94秒 / 1.5GB1.10秒 / 1.9GB
lobe-chat22.1秒 / 7.1GB15.4秒 / 9.5GB34.6秒 / 9.1GB

まず、シングルスレッド相当の--checkers 1でもdrizzle-ormは3.0倍、lobe-chatは5.1倍出ている。速くなった分の大半は並列化ではなくネイティブコード化によるもので、並列化の寄与は1.3〜1.4倍程度しかない。
公式ブログには、各ワーカーが共通の型情報を重複して持つ設計だと書かれている。ワーカーを増やすほど重複分のメモリと初期化コストを払うので、drizzle-ormのような小さめのプロジェクトでは8ワーカーの初期化が本体を上回り、既定の4より遅くなった。

次に、lobe-chatの--checkers 8は15.4秒→34.6秒と倍以上悪化した。ピークRSSが9GBを超えた状態で16GBの実メモリを他のプロセスと取り合い、スワップに入ったとみられる。
公式の「--checkers 8でvscodeが16.7倍」という数字は、今回の16GB機では再現しなかった。2つのリポジトリとも既定の4が最速だった。

ComfyUI_frontendでは動かない

Vue製のComfyUI_frontendにtscを素で当てると646件のエラーが出る。中身はすべて同じ種類だった。

src/components/actionbar/ComfyActionbar.test.ts(6,28): error TS2307:
  Cannot find module '@/components/actionbar/ComfyActionbar.vue' or its corresponding type declarations.

.vueファイルの型はvue-tsc(Volar)がTypeScriptのAPIに組み込む形で解決している。TypeScript 7にはそのAPIがまだない。

graph LR
    A[.vue SFC] --> B[vue-tsc / Volar]
    B --> C[TypeScriptのAPIを<br/>組み込んで拡張]
    C --> D[TS 6まで: JS実装<br/>APIあり]
    C --> E[TS 7: ネイティブ<br/>APIなし・7.1で新API予定]

公式もこの点は明言していて、Vue・MDX・Astro・Svelte系のワークフロー(このブログのAstroも含む)は当面TypeScript 6.0のまま。Angularには「CLIの型チェックだけTypeScript 7、エディタはTypeScript 6」という併用構成が提示されているが、Vue系はそれも使えない。
ComfyUI_frontendのvue-tscフルチェックはコールドで20.4秒(tsbuildinfoが残っていれば4.7秒)。この時間は7.1の新APIとVolar側の対応が出るまで変わらない。

今回測った4つのうち、TypeScript 7で速くなったのはプレーンなTypeScriptで完結している3つで、ComfyUI_frontendは新APIとVolar側の対応待ちとして残った。
公式は次の機能リリースを3〜4ヶ月後の7.1としている。