type-inference について
Biome で JavaScript と TypeScript の型推論に取り組む開発者向けのスキルです。推論の境界、Salsa クエリ、結果の意味、対象を絞ったテストを説明します。
主な機能
- 015 層の推論モデル
- 02生型と推論型のガイド
- 03絞り込んだリクエストとクエリ境界
- 04不確実性と結果の意味
- 05境界別のテストとプロファイリング
活用シーン
- 01構文由来の型情報の追加
- 02トラッキングクエリの作成・変更
- 03型対応 lint ルールの構築
- 04推論と無効化動作のテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ biomejs/ biome --skill type-inferenceインストール後、Claude Code で/type-inferenceを入力して読み込まれたか確認します。
詳細
Salsa による型推論
対象範囲
JavaScript および TypeScript の型推論、つまり生データの収集、Salsa による推論値、アナライザーリクエスト、追跡クエリ、推論固有のテストには、このスキルを使用してください。
一般的な lint の足場、診断、スナップショット、変更セットについては、対応するスキルを読み込んでください。CSS と HTML のモジュールグラフデータは、JavaScript または TypeScript の推論に直接関与しない限り、このスキルの対象外です。
関連ガイドを読む
推論タスクごとに、両方のアーキテクチャガイドを全文読み込む必要はありません。変更する境界に該当するセクションを読んでください。
| タスク | 基準となるガイド |
|---|---|
| 生データと推論表現、収集、推論レイヤー、ハンドル、正規化、作業上限 | biome_js_type_info/CONTRIBUTING.md |
| アナライザーリクエスト、追跡クエリ、解決コンテキスト、拡張、プロファイリング、Salsa 実行テスト | biome_module_graph/CONTRIBUTING.md |
両者の境界をまたいでアーキテクチャを変更する場合は、両方のガイドを読んでください。その後、変更対象となる具体的なリクエスト、クエリ群、表現の実装ファイルを調べてください。
メンタルモデル
型推論には 5 つのレイヤーがあります。
syntax and semantic collection
-> raw module tables
-> analyzer-facing requests
-> tracked Salsa queries
-> resolver helpers
収集処理はデータベースにアクセスせずに、1 つのモジュールを走査します。JsModuleInfo に生の型、式、バインディング型を記録します。リクエストは、アナライザー向けの結果契約を定義します。追跡クエリは、メモ化と無効化の境界を提供します。リゾルバーヘルパーは、それらの境界内で参照と推論済みの構造を評価します。
次の 3 つの型の世界を明確に区別してください。
| 世界 | 主な型 | 目的 |
|---|---|---|
| 生データコレクター | TypeData / RawTypeData、TypeReference、RawTypeId、TypeStore | モジュール内の構文、宣言、インポート、遅延式 |
| データベース連携 | InferredTypeData<'db>、LocalTypeHandle、GlobalTypeId | 追跡計算における推論値とモジュールの所有権 |
| アナライザー向け | InferredType<'db> | lint ルール向けの保守的で範囲が限定された検査 |
TypeReference は生データの世界に属します。InferredType で必要な操作がすでに提供されている場合、lint ルールで生の TypeData をパターンマッチしないでください。
推論データは、宣言元のモジュールが引き続き所有します。Arc の内側も含め、別のモジュールの推論ペイロードを現在のモジュールにコピーしないでください。参照、モジュール対応ハンドル、グローバル ID、追跡クエリを通じて所有権を維持してください。
最も限定的な境界を選ぶ
推論レベルは順番に実行するフェーズではなく、選択肢です。
| 必要な処理 | 境界 |
|---|---|
| 1 つのモジュールで収集された事実を検査する | 生データのローカルテーブル |
| 1 つの式、バインディング、エクスポート、メンバー、引数、分類を解決する | 対象を絞ったリクエストと追跡クエリ |
| モジュール内のすべての生の型、式、バインディングを解決する | モジュール全体の推論 |
境界が広いからといって、必ずしも正確になるわけではありません。複数のレベルで答えられる場合は、次の順序で判断してください。
inspect raw local information
-> return when the local result is conclusive
-> resolve the smallest selected reference
-> preserve uncertainty or widen only when the contract requires it
-> use complete-module inference as the last resort
infer_module_types は、完全なテーブルを必要とする契約に対応します。
infer_module_types_bottom_up は追跡されない外部スケジューラーであり、新しい追跡クエリから呼び出してはいけません。
型対応の lint ルール
型対応の JavaScript ルールでは、データベースクエリを直接使わず、アナライザーサービスを使用してください。
- ルールメタデータで
domains: &[RuleDomain::Types]を宣言します。 - ルールクエリとして
Typed<N>を使用します。 TypedServiceによって支えられているRuleContextの既存の推論メソッドを呼び出します。- 返された
InferredTypeを範囲限定ヘルパーで検査します。 Match、NoMatch、Indeterminateの分類を明示的に処理します。
crates/biome_js_analyze/src/services/typed.rs から始め、現在の Typed< の使用箇所を検索してください。ルールが 1 つのプロパティを尋ねる場合は、分類リクエストを優先してください。より限定的な分類器で答えられる場合は、完全な型を正規化して走査しないでください。
Raw Inference の変更
構文から導出される型情報を追加する場合:
- raw 表現を
crates/biome_js_type_info/src/type_data.rsで定義します。 crates/biome_js_type_info/src/local_inference.rsまたはcrates/biome_module_graph/src/js_module_info/collector.rsで収集し、 解決されていないオペランドはTypeReference値として保持します。crates/biome_js_type_info/src/interned_types.rsで変換します。- 汎用的な raw から inferred への変換では不十分な場合にのみ、
crates/biome_module_graph/src/db/type_inference/で評価処理を追加します。 - 新しいバリアントについて、raw と inferred のマッチ処理、走査、フォーマットを確認します。
コレクタースナップショットにより、データベースでの解決が必要な箇所では raw 構造が遅延されたままであることを確認します。クエリテストでは、推論結果を個別に確認します。
リクエストと追跡対象クエリ
リクエストは
crates/biome_module_graph/src/type_inference/requests/ にあります。個々の lint ルールではなく、再利用可能な結果の契約に対してリクエストを追加します。出力と不確実性の扱いが一致する場合は、既存のリクエストを再利用します。
リクエストでは、安定したメタデータ、単一の標準実行経路、正確なソースの起点、明示的な不確実性を定義します。操作は TypeInferenceRequestContext を通じて組み立てます。解析器のコードから低レベルのクエリ入力を構築しないでください。
結果に独立したメモ化と無効化の境界が必要な場合にのみ、追跡対象クエリを追加します。新しいクエリは次の条件を満たす必要があります。
- 利用側が必要とする最小限の意味的な結果を返す。
- データベースと、Salsa 入力またはインターン済みキーを 1 つ受け取る。
- 結果に影響する可能性がある依存関係だけを読み取る。
- 入力の欠落、曖昧さ、循環、処理量の上限の動作を定義する。
- 明確な結果を捏造せず、
UnknownまたはIndeterminateを保持する。 - 現行のクエリ計測ファミリーを使用する。
- 正確性と選択的実行のテストを備える。
同一の入力をインターンすると、共有 ID が割り当てられますが、クエリ結果がメモ化されるわけではありません。現行のトレイト、登録方法、テスト要件については、モジュールグラフガイドの Request architecture と Adding a tracked query のセクションに従ってください。
解決と結果の意味
オンデマンド解決では、リクエストがより広範な処理を必要とするまで、選択された検索経路に従います。正規化では、到達可能なハンドルを上限付きの走査で解決します。これはモジュール全体の完全な推論ではありません。名前空間の展開や、条件付きで行われるディープインポートのフォールバックによって、処理範囲が大幅に広がる可能性があるため、これらは明示的かつ観測可能な状態にしておく必要があります。
次の結果を混同しないでください。
| 結果 | 意味 |
|---|---|
None | 文書化された契約の下で、リクエストに結果がない |
InferredTypeData::Unknown | 推論により型が得られたものの、その構造は未確定である |
InferredTypeData::UnknownKeyword | ソースで TypeScript の unknown 型が明示的に使用されている |
TypeInferenceClassification::Indeterminate | 推論によって一致または不一致を証明できない |
TypeInferenceClassification::Match | 推論によって条件の成立が確実に証明される |
TypeInferenceClassification::NoMatch | 推論によって条件の不成立が確実に証明される |
不明または不確定な情報は、否定的な結果ではありません。誤検知の診断を避けるため、その情報を保持してください。これらの処理経路を変更する前に、標準の範囲拡大、循環の復旧、結果の意味に関するセクションを確認してください。
ResolutionCtx は、循環の追跡セット、深さの上限、キャッシュをコンテキストごとに保持します。解決処理中に別のモジュール用のコンテキストを作成し、そのコンテキストを通じて再帰しないでください。代わりに、追跡対象クエリ、使い切ったインポートまたはステップの予算、あるいは共有宣言評価器を使用してください。これらの境界については、モジュールグラフガイドの Resolver helpers のセクションに記載されています。
テストとプロファイリング
影響を受ける範囲のうち、最も狭い境界をテストします。
- 生レコードと参照にはコレクターテストを使用します。
- 結果、インポート、循環、不確実性にはリクエスト/クエリテストを使用します。
- 依存関係や無効化の範囲が変わる場合は、Salsa イベントテストを使用します。
- 最終的な診断動作には、アナライザーフィクスチャを使用します。
対象となるフローでは、無関係な編集によってクエリが再利用され、消費された入力によってクエリが再計算されることを証明してください。リクエストの契約またはテスト対象のフォールバックで必要とされない限り、モジュール全体の推論が実行されないことを検証してください。
リクエストによって想定以上のデータが解決される場合は、モジュールグラフガイドに記載されているメンテナンスプロファイルを使用します。リクエストとクエリの所要時間は包含関係にあるため、合算してはいけません。スナップショットの仕組みについては、testing-codegen を読み込んでください。
レビューチェックリスト
- 実装では、適切な生データ、推論済みデータ、またはアナライザー向けのワールドを使用している。
- リクエストまたはクエリは、契約を満たす最も狭い境界である。
- データの欠落、曖昧さ、循環、予算の使い切りによって不確実性が保たれる。
- 追跡対象のクエリ、消費済みの予算、または共有の宣言評価器による制限がないまま、解決中に
ResolutionCtxが作成されない。 - モジュールをまたぐ推論済みデータは、引き続きそのソースモジュールが所有している。
- 新しいリクエスト契約は再利用可能で、安定したメタデータを持つ。
- 新しいクエリキーと依存関係は、安定した Salsa 値である。
- モジュール全体の推論は存在しないか、明示的に正当化されている。
- 変更した境界を、正確性テストと選択的実行テストでカバーしている。