TSKaigi 2026で見た、TypeScriptエコシステムの変化── Go移植・Deno 2.8・AI生成スライドのトップ画像

TSKaigi 2026で見た、TypeScriptエコシステムの変化── Go移植・Deno 2.8・AI生成スライド

投稿日時:
maguroのアイコン

Deno Land Inc. / ソフトウェアエンジニア

maguro

Xアカウントリンク

みなさんこんにちは。

「あの人も読んでる」、第19回目の投稿です。maguro(X @yusuktan)がお届けします。

先日、TSKaigi 2026に参加してきました。TSKaigiはTypeScriptを中心としたカンファレンスで、今年は2026年5月22日(金)〜23日(土)に開催されました。僕自身も登壇し、DenoのTypeScript基盤が tsc から tsgoへどのように移行しつつあるのか、という話をしてきました。

そこで今回は、TSKaigi関連で気になった話題を取り上げるとともに、便乗してDeno 2.8のリリースノートと、そこから見えるDenoの今後の方針についてもざっくり見ていけたらなと思います。

TS 7: How We Got There

まず取り上げたいのは、TypeScriptチームのJake Baileyさんによる基調講演「TS 7: How We Got There」です(TSKaigi公式ページ)。

TypeScript 7については、Microsoftが2025年3月に公開したA 10x Faster TypeScriptの記事が大きな話題になりました。TypeScriptのコンパイラとTypeScriptのlanguage serviceをGoによるネイティブ実装へ移植することで、ビルド時間・エディタ起動・メモリ使用量を大きく改善する、というものです。

基調講演では、この「速くなります」という結果だけでなく、そこに至るまでの背景と移植プロセスが丁寧に説明されていました。

TypeScriptは単なるコンパイラではありません。tsc の型チェックに加えて、エディタのhover、補完、diagnostics、go-to-definitionといった体験も支えています。

従来のTypeScriptはTypeScript自身で書かれており、セルフホストには「チーム自身が毎日ドッグフーディングできる」「コミュニティがコンパイラにコントリビュートできる」という大きな利点がありました。

一方で、JavaScript/TypeScriptであることによる限界も見えていました。

  • JavaScriptではオブジェクトをスレッド間で共有できないため、マルチスレッド化しようとするとシリアライズのコストが大きい
  • async/await はfunction coloringを生み[1]、Promiseにもコストがある
  • VS Codeに同梱されるNode.jsを含め、多くのNode.jsビルドには4GBのメモリ制限がある[2]
  • tsserver は基本的に単一プロセスでリクエストを順番に処理するため、重い処理が詰まると後続のhoverや補完も待たされる

セルフホストすることのメリットと、TypeScript(JavaScript)が本質的に抱える限界。これらを天秤にかけた結果、別言語への移植という大きな決断を下すことになりました。

ただ、移植するにあたって難しいのが、TypeScriptにはコンパイラの挙動を完全に規定する独立した仕様書がないということです。「現在のコンパイラがどう振る舞うか」そのものが事実上の仕様になっています。だからこそ、構造を大きく変える“rewrite”(書き換え)ではなく、既存のコード構造・振る舞いをできるだけ保った“port”(移植)が必要になります。

その前提で言語選定を考えると、RustやZig、C#、OCamlなどが候補に挙がる中でGoになったことが納得できます。TypeScriptの旧コードベースがもともとクラス継承を使わずデータ・関数・インターフェース中心で書かれていたため、Goの構造にほぼそのまま移せます。加えて、goroutineによる並行性、GCがあることによる移植のしやすさ、そしてチームが数日で書けるようになる学習コストの低さが理由として挙げられていました。

「速い」だけでなく「まったく同じように振る舞う」ことをどう保証するか

キーノートを聞いていて僕が一番唸ったのは、この移植をどうやって「ちゃんと同じものになっている」と担保しているか、という話でした。速いネイティブ実装を書くだけでも相当な仕事ですが、それ以上にやっかいなのが、既存のtscとまったく同じように振る舞わせることです。

なにしろTypeScriptには仕様書がなく、いま動いているコンパイラの挙動そのものが仕様なわけで、移植版は型のつき方もエラーメッセージも、言ってしまえばクセまで含めて再現しないといけません。

その土台になっているのがbaseline(スナップショット)です。TypeScriptのテストは、関数単位でアサーションを書くのではなく、コンパイラに入力を与えて出力をまるごとファイルに保存する方式が中心で、この高レベルなcompiler testは約17,000件あるそうです。

AST上の各ノードを歩いて「このノードの型は何か」を書き出したものがtype baseline、エラーメッセージと位置(波線付きの表示)を書き出したものがerror baseline。いずれもリポジトリにコミットされ、挙動が変わればPRのdiffとして見えます。

移植では、これをもう一段メタにします。JS版とGo版を同じ入力で走らせ、その出力の差分自体をbaselineとしてコミットするのです。Go版が未完成なうちは差分だらけですが、移植が進むほど差分は減る。Jakeさんは実際のPRを見せながら「このdiffファイルが削除された=そこは旧コンパイラと一致した」と、差分を一つずつ潰していく様子を説明してくれました。

他に紹介されていたのが、differential fuzzingです。Goにはカバレッジベースのfuzzingが標準で入っているので、関数にランダム入力を投げてクラッシュを探せます。

ただ、移植で本当に効くのはそこから一歩進めた使い方です。たとえば旧コンパイラからそのまま移植したある処理は正規表現を使っていたのですが、Goの正規表現エンジンは遅いので[3]、正規表現を使わない速い版に書き換えたかったそうです。

でも、その置き換え後の実装が本当に同じ結果を返すかは自明ではありません。そこで、新旧2つの関数に同じランダム入力を与えて出力を比較するfuzzテストを書いて、食い違えば、その入力がそのままバグの再現ケースになります。いくつか例を与えれば、あとはfuzzerが反例を見つけてくれる、というわけです。

言語サーバも実物でテストしています。ランダムなLSPリクエストを大量に投げてクラッシュを探し、各クラッシュにはreplay手順がつきます。新コードではそのreplayが「ただのGoのテスト」として書けて、モックも複雑なharnessもなく、プロセス内で本物のLSPサーバを立ち上げ、fuzzerが送ったのと同じメッセージを流すだけで再現できます。

加えてGitHub上位のTypeScript/JavaScriptプロジェクトを、実際のtsconfigごと走らせる実地検証もあります。TypeScript本体の変更をVS Codeのような巨大なコードベースに当てると、以前は必要だった ts-expect-error が不要になったり、到達不能コードが新たに検出されたりすることがある。

そうした差分を通じて、変更が机上のテストだけでなく、現実の利用者コードにどのような影響を与えるかを確認できる、という話でした。

移植というと、つい「新しい実装をどれだけ速く書けるか」に目がいきがちですが、本当の難所は「すでにいる大量のユーザーの期待を裏切らないこと」のほうにあります。snapshotやdifferential fuzzing、人気リポジトリでの実地検証でそこを地道に詰めていく話は、TypeScriptに限らずいろんな移植・再実装に効く考え方だなと、勉強になりました。

質疑応答では、僕から「AIによる移植支援が本格的に使えるようになったのはいつごろだったのか」という質問もしました。Jakeさんの答えは、ざっくりいうと2025年の終わりごろから実用感が出てきた、というニュアンスでした。これはClaude Opus 4.5が出てきた時期とも重なっていて、僕自身の体感ともかなり近いです。移植に限らず、AIが長めの文脈を追いながら自律的に複雑な作業を進める道具として、ぐっと信頼できるようになった感覚があります。

この感覚は僕やJakeさんだけのものでもなさそうです。Burke HollandさんもOpus 4.5 is going to change everythingで、Opus 4.5を使って考えが変わったと書いています。Opus 4.5前後でコーディングエージェントへの信頼感が一段変わった、という世間の空気をよく表しています。

移植に関連する話として、BunがZigからRustへ移った話も最近盛り上がっていました。PR oven-sh/bun#30412では、既存のテストスイートを全プラットフォームで通しつつ、アーキテクチャやデータ構造は大きく変えずにRustへ移した、と説明されています。

後続PRを見ても、Zig由来の構造をRustへ持ち込んだうえで、必要なところは unsafe や明示的なlifetime contractとして整理していました。TypeScriptのGo移植と同じく、既存の挙動を壊さずに移植することが中心にある話だと感じました。

AIエージェントを活用して既存の何かを移植する際、参考になりそうなアプローチです。

Deno 2.8と、僕の発表で話した「TypeScriptとの境界」

TypeScriptのGo移植の話を聞きながら、僕は自分の発表で話したDenoのTypeScript基盤のことも考えていました。

DenoはTypeScriptそのものを作っているわけではありませんが、TypeScript checkerやlanguage serviceとはかなり深く付き合わざるを得ないランタイムです。npm:[4]jsr:[5]、HTTP(S) importなど、普通のTypeScriptプロジェクトにはない世界をTypeScript側にどう扱ってもらうか、という問題があります。

発表では、DenoがJavaScript版TypeScriptを内蔵してV8 Isolate内で走らせていること、そしてTypeScript本体がGo実装へ向かう流れの中で、Denoも一時期forked typescript-go を使う実験をしていたことに触れました。

いま見えている方向性は、Deno側がMicrosoft公式のTypeScript実装の前提に寄せていく、というものです。TypeScriptにパッチを当てて半ば無理矢理Denoの独自性を理解させるのではなく、JSR packageやremote moduleなどをローカルのファイルとして配置し、標準的なTypeScriptツールがそのまま理解できる形に整える(denoland/deno#33163)、ということです。

この流れを頭に置きつつ、先日リリースされたDeno 2.8の変更点を軽くご紹介できたらと思います。Deno固有の世界と標準的なTypeScript/Node.jsの世界の境界をどうならしていくか、という動きをざっと見ていきます。

Deno 2.8は標準エコシステムへ寄せてきている

TSKaigi初日の2026年5月22日にDeno 2.8が公開されました。公式ブログでは“biggest minor release to date”と表現されており、かなり大きなリリースです。

このリリースの中で今回の話と特に関係するのは、TypeScriptまわりとNode.js互換性の改善です。

たとえば、Deno 2.8では deno adddeno install が、CLI上では npm: プレフィックスなしの名前をnpmパッケージとして扱うようになりました。

リリースノートでは、deno install が既存のNodeプロジェクトにおける npm installyarnpnpm install の代替として使えること、package.json を読み、互換性のある node_modules layoutを書き出すことが説明されています。Deno独自の指定子を前面に出すのではなく、Node.js開発者がすでに慣れている作法へCLI体験を寄せているわけです。

さらに、deno check とLSPで lib.node がデフォルトで含まれるようになりました(denoland/deno#33823)。

これまでは NodeJS.Timeout(setTimeout()の戻り値として出てくる型)や Bufferprocess などのNode.js ambient typeを解決するために、deno.jsoncompilerOptions.lib"node" を追加したり、/// <reference types="node" /> を書いたりする必要がありました。

Deno 2.8では、それらがデフォルトで見えるようになります。

Deno 2.8ではNode.js自身のテストスイートに対するpass rateも大きく改善し、公式ブログによるとDeno 2.7の約42%からDeno 2.8では76.4%(4,457件中3,405件)まで上がっています^node-pass-rate。

deno packdeno cideno install の改善なども含めて、Denoが独自の世界だけで完結するのではなく、TypeScript、npm、Node.jsの既存エコシステムとより自然につながろうとしているのがわかります。

Denoはもともと、Ryan Dahlさんが10 Things I Regret About Node.jsで語っていた npmnode_modules、暗黙に広い権限、ブラウザと距離のあるモジュール解決といった設計上の後悔を見直すところから始まったランタイムです。

初期のDenoには、Node.js/npmの慣習から意識的に距離を取り、別の前提でJavaScript/TypeScriptランタイムを作り直そうとする方向性が強くありました。Deno 2.8の変更からは、Denoらしさを保ったまま既存エコシステムとの摩擦を減らしていく姿勢が見えます。

AI時代の発表資料で、人間が引き受ける部分

最後に、TSKaigi関連でもう1つ、mizdraさんの記事「TSKaigi 2026 の発表資料の体感半数以上が AI 生成感あるものだった」を取り上げます。

記事では、TSKaigi 2026でAI生成と見られる発表資料が多数あったこと、その特徴として「ダッシュが多い」「会場の大きさに対して文字が小さすぎる」「文字やイラストを詰め込みすぎている」「複数カラム構成が多い」「日本語・英語の併記が装飾的に使われている」などが挙げられています。

mizdraさんは、AIを使ってスライドを作ること自体は肯定しています。AIは時短にもなるし、これまで発表に踏み出しづらかった人の背中を押すこともあります。僕自身も、資料作成や記事執筆の過程でAIに壁打ち相手になってもらうようになっています。

ただ、mizdraさんが本当に問題にしているのは、AIを使ったこと自体ではありません。AIが出してきたものをそのまま壇上に載せてしまって、肝心の聞き手のほうを向けていない、という点だと思います。

AIが出したアウトラインやスライドは、いったん自分で噛み砕いて、自分の発表として責任を持てる形に翻訳してあげる必要があります。僕はAIの助けを借りつつスライドを作り、その後実際に口に出しての発表練習を繰り返しました。繰り返す中で「自分の言葉として素直に口から出てくるか」というところを確認しつつ、修正を重ねていきました。

ただ、このやり方が絶対的な正解だとも思いません。自分自身、大きなカンファレンスで30分枠の発表をするのは初めての経験だったこともあり、手探りしながらの準備になりました。引き続き、試行錯誤したり、他の方の発表から発表の仕方を学んだりしながら、良い発表のスタイルを模索していければと思います。

おわりに

今回はTSKaigi 2026を起点に、TypeScript 7のキーノート、Deno 2.8、そしてmizdraさんのAI生成スライドの記事を取り上げました。

自分も登壇しつつ、空き時間はセッションやキーノートを聴いて回り、ずらりと並んだスポンサーブース(本当にたくさんありました!)を覗いては中の人と話し込む、という具合で、会場全体の熱気がとにかくすごかったです。

懇親会では出張寿司やクラフトビールを片手に、普段オンラインでしか接点のない人や初めましての人ともたくさん話せて、これが一番の収穫だったかもしれません。やっぱりオフラインのカンファレンスはいいなと改めて感じる2日間でした。

今後も各種カンファレンスやミートアップなどには積極的に参加していきたいです。

また次回、おすすめコンテンツを紹介していきます。お楽しみに!

maguroさんの「も読」過去記事

執筆者へのお便り

脚注
  1. function coloringはBob NystromさんのWhat Color is Your Function?由来の言い回しで、async な関数は基本 async からしか自然に呼べず、await のために呼び出し側も芋づる式に async になっていき、その「色」がコード全体へ伝播する、という意味です。

  2. V8のpointer compressionが関連します。pointer compressionを有効にしてビルドされたV8では、オブジェクトへのポインタを4GBの領域(cage)内の32bitオフセットとして表現するため、--max-old-space-size をいくら大きくしてもヒープは約4GBを超えられません。VS Codeに同梱されているNode.jsもこのpointer compression有効ビルド(ElectronのV8 memory cage解説)なので、巨大なASTや型情報をメモリに抱えるコンパイラだと、この天井に当たってOOM(Out Of Memory)になりやすい、ということのようです。

  3. ここは個人的に少し意外でした。Jake BaileyさんのPR microsoft/typescript-go#1483では、JS版ではregexで書かれていた matchFiles について「JSではregexが速いが、Goではそうでもない。特にECMAScript互換のために regexp2 を使うとさらに重い」と説明されています。後続の#2407では、regexp2 を使わない実装に置き換えることで、ReadDirectory 系ベンチマークのgeomeanで約85%の実行時間短縮が報告されています。

  4. Denoでは import OpenAI from "npm:openai" のように、import文の中で npm: プレフィックス付きの指定子を書くだけでnpmパッケージを読み込めます(Deno公式ドキュメント: Node and npm modules)。package.jsonnode_modules を前提にせずURL指定子の延長として解決する仕組みで、普通のTypeScriptプロジェクトから見ると見慣れない形です。

  5. jsr: は、Deno社が中心となって立ち上げたTypeScriptファーストのパッケージレジストリ JSRを指す指定子です。 import { encodeBase64 } from "jsr:@std/encoding" のように書くと、JSRからモジュールを読み込めます。npm向けのTypeScript製パッケージではビルド済みJavaScriptと .d.ts 型定義を配布する形が一般的ですが、JSRではTypeScript/ESMを前提に、型情報を含むTypeScriptソースをより直接的に公開・利用できます。