takehironegishitakehironegishi

TSKaigi 2026 参加レポート

2026-06-01

はじめに

2026 年 5 月 22 日(金)と 23 日(土)の 2 日間にわたり開催された TSKaigi 2026 に現地参加しました。 本記事では、参加したセッションに関する感想や学びをレポートします。

こうしたカンファレンスに現地参加するのは私自身初めてでしたが、参加者の熱気や盛り上がりを直接感じられたり、ブース周辺では出展者の方と会話できたりと、オンライン参加とは全く異なる体験ができ、是非次回以降も現地参加したいです。 運営の方々及びスピーカーの方々をはじめ、関わってくださった方々にこの場を借りて感謝をお伝えしたいです!

Day 1 (5/22)

業務に残された「よくない型」で考える「TypeScriptの難しさ」

私自身も身に覚えがある内容でしたし、他の参加者も頷く様子がよく見られたセッションでした。 基本的には TypeScript の恩恵が受けられなくなるような any や assertion、コメントによる型チェックの無効化は避けたいものですが、どうしてもそれが難しい場合に出くわすこともあります。 セッション内で挙げられていた Array.includes や TS 5.4 以前の Array.filter の使用時は、ユーティリティ関数を自作したのも記憶に新しいです。

そうしたよくある妥協や失敗パターンを、(アプリケーションと DOM イベントやレスポンスをはじめとした外部との)境界由来と(TypeScript の言語仕様としての)内部由来とに分類し、それぞれどのように対策できるかを例示していただいた点が大変分かりやすかったです。 またそうした対策をどのように運用していくかのルールにまで触れられており、チームにも展開しやすいセッションであったことが印象的でした。

パターンと対策をリファレンス化した上で AI 用のナレッジ/ルールに組み込めば、AI が同様のパターンに遭遇した場合も妥協パターンを回避できそうですし、そういったスキルを育てていき、より型安全な開発を進めていきたいですね。

登壇資料

型の深宇宙へ飛び込め ─ tscを遅くする記述パターンの全解剖

TypeScript の開発中、エディタ上での型解決や CLI での tsc 実行に時間がかかっているなと感じたことがあるのは私だけではないはずです。 このセッションでは、なぜコンパイルに時間がかかるのか、その理由と解決策に焦点が当てられていました。実際にその解決策を適用することで、どれほど改善されるのかがまとめられており、大変参考になりました。

以前、ライブラリ (SWR) の hook をラップしたカスタム hook をメンテナンスしたことがあるのですが、openapi-typescript から生成された関数を型安全にラッパーで扱うために再帰型を導入したところ、tsc が重くなった経験があります。 その際に今回のセッションのような知識があればより最適化できていたかもしれませんし、今後も活かせる内容でした。

セッション内でも触れられていましたが、TypeScript v7 時点でも v6 とアルゴリズムに変更はないとされています。今後も TypeScript の型の柔軟性を活かしつつより速く開発するために、こうしたコンパイラの知識も日々アップデートしていきたいと思います。

登壇資料

Oxlint は ESLint / typescript-eslint を置き換えられるか?

Oxlint、やはり速いですねと再認識しました。 VoidZero、および VoidZero から展開されている Oxc については以前から個人的に注目しており、私が所属するチームのフロントエンドにおいても、formatter は Oxfmt を使用しています。(Prettier から移行した際には、本当に format されているのか不安になるくらい実行が速かった)

セッションでも触れられていたように、2026/05/22 時点では Type-Aware Linting も含めると完全に ESLint から移行できるわけではないものの、セッションの中で触れられていたサンプルプロジェクトの実測値としては、Type-Aware Linting なしの場合は約 27 倍の高速化、ありの場合は約 20 倍の高速化が見られたようです。 プロダクションコードともなると走査するコードベースも大きくなっていきますが、そうしたコードベースにこそ効果が高いものだと言えますね。

試しに私の方でもプロジェクトの Linter をざっと ESLint から Oxlint に置き換えて(Type-Aware Linting なしで)試してみたところ、約 35 倍速くなりました。 ルールの互換性も高まっていますし、今後の置き換えを積極的に試していきたいと思います。

登壇資料

TS 7: How We Got There

Microsoft の TypeScript チームに所属している Jake Bailey 氏の基調講演ということもあり、会場も満員となっていました。

TypeScript で書かれていた TypeScript のコンパイラをなぜ Go で書き直すに至ったのか、TypeScript と Go の Structural Similarity や Concurrency without async/await などの側面から理由が述べられ、改めて Go の採用に至った背景が腹落ちする内容でした(こちらの discussion でも詳細に触れられています)。

また移行時に採用したテスト手法についても非常に勉強になりました。 コンパイラの出力の差異がないかを検証する Snapshot Testing はもちろん、Fuzzing や Differential Fuzzing(新旧実装を比較して正確性を検証する手法)も採用されたとのことでした。(Go における Fuzzing のドキュメント) テストケースを人手で揃えるのではなく自動生成することで、人力では考慮しきれない移行時のコンパイラのバグを検出できるようにしていたそうです。 こうした大規模な移行をする場合、いずれも参考にできるテスト手法ですね。

デモの中で tsc と tsgo の速度の比較を実際にされた際には、tsc が全然終わらずに会場がざわざわする瞬間が何度かありましたが、その分 tsgo の実行がすぐに終わった際には会場が歓声に包まれ、Jake 氏も嬉しそうだったのが印象に残っています。

また実際に tsgo を試している企業の声も紹介されていましたが、Slack の場合だと CI が 7.5 min → 1.25 min に、VS Code のコンパイルは 125.7s → 10.6s に高速化されるなど、大きく改善した事例も紹介されていました。

コンパイラを Go に移植した初めてのメジャーバージョンとなる TypeScript v7 は 2026/05 末時点ではベータ版ですが、安定版がリリースされたら早速組み込みたいと感じられる基調講演でした。

登壇資料

Day 2 (5/23)

TypeScriptはどのようにどこまで推論できるのか ─ とにかく as は禁止で

TypeScript を使っている以上、とにかく as は禁止にしたいというのは誰しも思うことではないでしょうか。 このセッションでは、TypeScript の型推論力やその進化、またどうしても必要な時は影響を最小限にするためにヘルパ関数に切り出そう、といったように、これも非常に同意できる内容でした。 印象的だったのは、ユーティリティ型として提供されている NoInfer の使い道でしたね。 以下 2 つのコード例は登壇資料からの抜粋です。まず 1 つ目の例では、x は通常 'click' として推論されます。

function filter<T>(x: T[], callback: (arg: T) => boolean): T

const foo: 'click' | 'hover' = filter(['click'], x => ...); // x: 'click'

一方、NoInfer を使うことにより、次の場合の x は 'click' | 'hover' となるようです。(その名の通り、引数からの型推論の対象から除外される)

function filter<T>(x: NoInfer<T>[], callback: (arg: T) => boolean): T

const foo: 'click' | 'hover' = filter(['click'], x => ...); // x: 'click' | 'hover'

あまり使い所がわかっていなかったですが、Zodrefine のように、引数で渡されるコールバック関数の引数型から推論させるのではなく、スキーマ側の型情報を優先させたい、といったときに活用できるようです。 これは大変参考になりました。

なお、資料の中で []any[] になるというのは驚きでした。私も完全に never[] になると思っていましたが、確認してみると確かに any[] となっており、いつからか変わったんでしょうかね...

とにかく、as は原則禁止、どうしても必要な場合はヘルパ関数に切り出す、という点は今後意識していきたいですね。

登壇資料

Real World Effect-TS: 堅牢なプロダクトを型で組み上げる

Effect-TS は実際にどのように使われているのか、またセッションの冒頭にある問い「TypeScript は中規模以上のバックエンド開発に耐えるのか?」はまさに私が気になっていたところでした。

Promise を Effect 型に置き換えるというコンセプトに加え、成功時の値や起こりうるエラーを型で表現できる点には Result 型に通じる発想を感じました。さらに Effect<A, E, R>R で Effect の依存まで表せるという仕組みには初めて触れ、非常にユニークでした。 また内部では Fiber を採用することで処理単位ごとの並行性や実行途中でのキャンセルも可能ということで、まさに React の Fiber アーキテクチャや Suspense とも通ずるものがあり、React を普段触っている私からするとその概念に親近感を覚えました。

一方でセッション中でも触れられていた通り、Promise を Effect に置き換えると、アプリケーション全体が Effect-TS に強く依存する形になります。試験的に導入することが難しく、まさに「心中する覚悟が要る」フレームワークであると感じます。

チーム全体でその覚悟を持ち、Effect-TS の利点をフルに活用することができれば、TypeScript バックエンドにおける強力なフレームワークの候補として上がるのではないでしょうか。

登壇資料

Polymorphic Components パターンで作る、型安全でセマンティックな UI コンポーネント

弊社のソフトウェアエンジニアである ryo の登壇でした。 (元記事はこちら)

デザインシステムを実装する際に、React の場合は UI コンポーネントが返す JSX に HTML タグを直書きしてしまうと、そのコンポーネントは指定した HTML タグに定まってしまう、という問題がありますね。私も昔経験があり、最初は HTML タグごとにコンポーネントを分けてしまう、という方針をとったことがあります(結果、似たようなコンポーネントが爆増しました)。

これに対する解決策は asChild や render props、またセッションで紹介された as(TypeScript の型 assertion とは別物の prop なので注意)がありますね。

資料の例にあるように、次のコードでは as によって最終的な成果物となる HTML が属性含めて型安全になります。 コンポーネントの再利用性を保ちながら型の安全性を保てる点で、強力な機能だと言えるでしょう。

interface TextOwnProps<T extends ElementType = "span"> {
  as?: T;
}

type TextProps<T extends ElementType = "span"> = TextOwnProps<T> &
  Omit<ComponentProps<T>, keyof TextOwnProps<T> | 'className'>;

<Text>本文テキスト</Text> // <span>本文テキスト</span>
<Text as="p">本文テキスト</Text> // <p>本文テキスト</p>

UI ライブラリを導入する場合はこの as のような機能が提供されていることも多いですが、自前でデザインシステムを構築する場合に覚えておきたいですね。

登壇資料

childrenの順序まで型で縛る ── Branded Typesで実践するJSXの構造安全

今回の TSKaigi では Branded Types を扱うセッションもいくつかあり、その中でもこちらは「JSX の構造の安全性を Branded Types で実現する」という点でユニークなものでした。

Parent コンポーネントの内部に Child コンポーネントのみを含められるようにしたい、という制約はどう実装すべきでしょうか。React の場合 Compound Components パターンを採用して Context を共有する設計にすれば、Child を Parent の中でしか使えないようにする(直接使用すると error を throw する)ことは可能です。しかし逆方向、つまり「Parent の中には Child しか配置できない」という縛りをどう実現するかは、自分の中で解決できていませんでした。

それに対して、以下コードのように子コンポーネントの型を Branded Types として定義し、それを親コンポーネントの children Props とすると、型と一致しない場合は資料にあるようにエラーとなってくれました。 (以下は、資料の例を編集しています。また、TypeScript v6.0.3 における挙動です)

// 型定義
declare const __brand: unique symbol;
type Brand<T, B extends string> = T & { readonly [__brand]: B };

type HeaderProps = {
  title: string;
};
type BodyProps = {
  children: ReactNode;
};

type CardHeaderEl = Brand<ReactElement<HeaderProps>, "CardHeader">;
type CardBodyEl = Brand<ReactElement<BodyProps>, "CardBody">;

type CardProps = {
  children: readonly [CardHeaderEl, CardBodyEl];
}

// コンポーネント定義
function CardHeader(p: HeaderProps) {
  return (
    <header>
      <h2>{p.title}</h2>
    </header>
  ) as unknown as CardHeaderEl;
}

function CardBody(p: BodyProps) {
  return (<section>{p.children}</section>) as unknown as CardBodyEl;
}

function Card({ children: [header, body] }: CardProps) {
  return (
    <article>
      {header}
      {body}
    </article>
  );
}

// OK
function OkExample() {
  return (
    <Card>
      {/* 関数呼び出しにする必要あり */}
      {CardHeader({ title: "Hello" })}
      {CardBody({ children: <p>World</p> })}
    </Card>
  );
}

// NG (期待とは逆順に Body → Header と配置した例)
function NgExample() {
  return (
    <Card>
      {/* 以下のエラーが発生。
        Type 'CardBodyEl' is not assignable to type 'CardHeaderEl'.
        Type 'CardBodyEl' is not assignable to type 'ReactElement<HeaderProps, string | JSXElementConstructor<any>>'.
        Types of property 'props' are incompatible.
        Property 'title' is missing in type 'BodyProps' but required in type 'HeaderProps'.
      */}
      {CardBody({ children: <p>World</p> })}
      {CardHeader({ title: "Hello" })}
    </Card>
  );
}

注意点として、セッションでも触れられていたように、コンポーネント呼び出しではなく関数呼び出しにする必要がある点に少々癖があると感じました。また、Hooks / Suspense / Context 等が使えなくなる点もあり、実装上のデメリットは無視できないようです。

ただこのような Branded Types の活用方法は面白いなと思いましたし、他にも活かせるケースがあるかもしれません。

登壇資料

おわりに

ブログを書きながら改めて振り返ってみると、非常に多くを学べた 2 日間でした。 また現地参加特有の熱量に私も感化され、TSKaigi 後すぐにプロジェクトの TypeScript のメジャーバージョンを v6 に上げました。 (以前から v6 に上げたいとは思いつつ、v5 が peer dependencies に指定されたライブラリがあり中々上げられていませんでした。が、TSKaigi の休憩中に確認したらその問題が解決していたので...)

改めて、このような場・体験を作り上げていただいた皆様に感謝申し上げます。

Belong でも、フロントエンドを TypeScript で開発しており、TSKaigi で学んだことを活かせる場が多いです。 弊社に興味を持っていただけましたらエンジニアリングチーム紹介ページをご覧いただけると幸いです。 最後までお読みいただき、ありがとうございました!

No table of contents available for this content