Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】--incremental Made My TypeScript Hook 3.6x Slower. A 200KB Threshold Fixed It

2026年09月17日に「Dev.to」が公開したITニュース「--incremental Made My TypeScript Hook 3.6x Slower. A 200KB Threshold Fixed It」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

TypeScriptの型チェックを高速化する`--incremental`オプションは、大規模プロジェクトではキャッシュファイル肥大化で遅くなる場合がある。そこで、キャッシュファイルが200KBを超えたら`--incremental`を無効にして通常モードに切り替える仕組みを導入した。これにより、効率的な型チェックを実現し、開発速度を向上させた。

ITニュース解説

システムエンジニアを目指す皆さんに、開発現場で直面する可能性のある実践的な課題と解決策について解説する。この記事は、自動的にコードを修正するAIエージェントとTypeScriptプロジェクトを組み合わせた開発環境で、型チェックの効率化を試みた際の経験談だ。

コードを自動で修正するAIエージェントにとって、自分が書いたコードが正しいかどうかの「検証」は非常に重要だ。人間がコードを書く場合、型エラーが発生すればすぐに気づき修正できるが、AIエージェントの場合、検証に時間がかかると、その間AIは次の作業に進めず、非効率になってしまう。特に、TypeScriptの型チェックを行うtsc --noEmitコマンドは、プロジェクトの規模が大きくなると、完了までに30〜60秒もかかり、これがAIエージェントの作業の大きな妨げになることが判明した。これは、tscが起動するたびにプロジェクト全体の依存関係をすべて読み込み直すためで、いわゆる「コールドスタート」が重いことが原因だった。

この遅延を解消するため、TypeScriptコンパイラが提供する--incrementalオプションの導入が検討された。このオプションは、初回実行時にプロジェクトの完全な解析を行い、その結果を.tsbuildinfoというファイルにキャッシュする。これにより、2回目以降の実行では、変更があったファイルのみを解析する差分チェックが可能になり、通常は1〜3秒で完了するため、フック(特定のイベント発生時に自動実行されるスクリプト)での利用に最適だと考えられた。

しかし、実際に大規模なプロジェクトに--incrementalを適用したところ、予期せぬ問題が発生した。初回実行時のキャッシュ生成に時間がかかりすぎるだけでなく、プロジェクトの成長とともに.tsbuildinfoファイルが肥大化し、その巨大なファイルを毎回読み込むオーバーヘッドが、差分解析のメリットを上回ってしまったのだ。結果として、--incrementalを使うとコールドスタート時の型チェックが3.6倍も遅くなるという逆効果が生じてしまった。小規模なプロジェクトでは効果的だった--incrementalが、大規模プロジェクトではかえってパフォーマンスを悪化させるという、両者の関係が逆転する現象が起きたわけだ。

この問題を解決するため、筆者は「プロジェクトの規模に応じて、型チェックのモードを自動で切り替える」というアイデアにたどり着いた。.tsbuildinfoファイルのサイズがプロジェクトの規模と強く相関することに注目し、「キャッシュファイルが200KBを超えたら、--incrementalを無効にして通常のtsc --noEmitモードにフォールバックする」というルールを考案した。この200KBという閾値は、実際のプロジェクトでの計測に基づいて設定されたもので、小さいプロジェクトでは--incrementalが高速で、大きいプロジェクトでは通常モードが高速になる境界値として機能する。

この自動切り替えの仕組みは、シェルスクリプトとして実装された。スクリプトはまず、現在のディレクトリにtsconfig.jsonが存在するかを確認し、存在しない場合はTypeScriptプロジェクトではないと判断して即座に終了する。次に、node_modules/.cache/tsc-hook.tsbuildinfoというキャッシュファイルのサイズをチェックする。このファイルは、プロジェクトルートを汚さないよう、一般的な.gitignoreで管理されるnode_modules/.cacheディレクトリ内に配置され、名前も他のビルドキャッシュと区別されるように配慮されている。ファイルのサイズが204800バイト(200KB)を超えていれば通常モード、それ以下であれば--incrementalモードでTypeScriptコンパイラを実行する引数を生成する。

tscコマンドの実行には、--noEmit(JavaScriptファイルを生成しない)、--pretty false(色付き出力を無効化し、AIエージェントが読みやすいようにプレーンテキストにする)といったオプションが常に含まれる。また、型チェックが無限に続くことを避けるため、timeoutコマンド(macOSではgtimeout)を使って60秒の制限時間を設けている。

特に重要な設計思想として、「フックは情報の伝達役であり、処理の流れを制御するものではない」という点がある。たとえ型エラーが検出されたり、タイムアウトで処理が中断されたりしても、スクリプトは常にexit 0(成功)で終了する。これは、AIエージェントの作業フローを妨げないためだ。エラーメッセージは標準出力に書き出され、AIエージェントはそれを読み取って「型エラーがあるから修正しよう」と自律的に判断し、次の行動に移る。これにより、人間が型エラーに気づくまでのタイムラグが実質的にゼロになり、AIエージェントはスムーズに修正ループに入ることができる。

この開発過程では、いくつかの具体的な問題に直面した。例えば、パイプを使ったコマンド実行では$?が常にパイプの最後のコマンド(通常はhead)の終了コードを返すため、tscのエラーを正しく検出できない問題があった。これは、timeoutコマンドがtscの終了コードを透過してくれることで意図せず解決された。また、macOSとLinuxでstatコマンドのオプションが異なるため、ファイルサイズ取得に手間取ったことや、exit 1を設定するとAIエージェントの処理が中断されてしまうこと、AIエージェントが読み取るログにANSIエスケープコード(ターミナルでの色付けに使われる特殊文字)が混入し、誤った判断を下すことがあった。さらに、AIエージェントが複数のツールを並行実行する際に、複数のtsc --incrementalプロセスが同じ.tsbuildinfoファイルを同時に書き込もうとしてファイルが破損する問題も発生した。これらの問題は、適切なコマンドオプションの選択、OS差異への対応、そしてAIとの連携における設計思想の見直しによって解決されていった。

この記事は、小さなシェルスクリプトがいかに開発のボトルネックを解消し、月120万円の収益を上げるAIエージェントの自律的な開発環境を安定稼働させているかを示している。プロジェクトの規模によって最適なツールや設定は変わるものであり、常に「計測」を通じて自分のプロジェクトに合った最適な閾値を見つけることが重要だ。安易に「魔法の数字」を流用するのではなく、自身の環境で実際にデータを取ることが、効率的な開発の鍵となる。

関連コンテンツ

関連IT用語