关于 type-inference
面向 Biome 贡献者的 JavaScript 和 TypeScript 类型推断开发技能,涵盖推断边界、Salsa 查询、结果语义与针对性测试。
主要功能
- 01五层推断模型
- 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 |
跨越两份指南所述边界更改架构时,请同时阅读两份指南。然后检查与具体请求、查询系列或表示相关的实现文件。
心智模型
类型推断分为五层:
syntax and semantic collection
-> raw module tables
-> analyzer-facing requests
-> tracked Salsa queries
-> resolver helpers
收集过程遍历单个模块,不访问数据库。它会在 JsModuleInfo 中记录原始类型、表达式和绑定类型。请求定义面向分析器的结果约定。跟踪查询提供记忆化和失效边界。解析器辅助函数在这些边界内评估引用和推断结构。
请区分以下三种类型世界:
| 世界 | 主要类型 | 用途 |
|---|---|---|
| 原始收集器 | TypeData / RawTypeData、TypeReference、RawTypeId、TypeStore | 模块本地语法、声明、导入和延迟表达式 |
| 数据库支持 | InferredTypeData<'db>、LocalTypeHandle、GlobalTypeId | 跟踪计算中的推断值和模块所有权 |
| 面向分析器 | InferredType<'db> | 为 lint 规则提供保守且有界的检查 |
TypeReference 属于原始类型世界。如果 InferredType 已提供所需操作,lint 规则就不应对原始 TypeData 进行模式匹配。
推断数据仍归声明它的模块所有。不要将其他模块的推断负载复制到当前模块中,包括通过 Arc 复制。请通过引用、模块感知句柄、全局 ID 和跟踪查询保留所有权。
选择最窄的边界
推断级别是可选方案,而不是连续阶段:
| 需求 | 边界 |
|---|---|
| 检查单个模块中收集的事实 | 原始本地表 |
| 解析单个表达式、绑定、导出、成员、参数或分类 | 定向请求和跟踪查询 |
| 解析模块中的所有原始类型、表达式和绑定 | 完整模块推断 |
边界更宽并不一定更正确。多个级别都能满足需求时,请按以下顺序决策:
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>用作规则查询。 - 调用
RuleContext中现有的推断方法,该方法由TypedService提供支持。 - 通过有界辅助函数检查返回的
InferredType。 - 将分类结果显式处理为
Match、NoMatch或Indeterminate。
从 crates/biome_js_analyze/src/services/typed.rs 开始,并搜索当前的 Typed< 使用者。规则只询问一个属性时,优先使用分类请求;如果更窄的分类器即可回答问题,就不要规范化并遍历完整类型。
更改原始推断
添加由语法推导的类型信息时:
- 在
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中进行转换。 - 仅当通用的原始类型到推断类型转换不足以满足需求时,才在
crates/biome_module_graph/src/db/type_inference/下添加求值逻辑。 - 检查原始类型和推断类型的匹配、遍历及格式化逻辑,确保覆盖新变体。
收集器快照可证明:在需要数据库解析的场景中,原始结构仍会延后处理。查询测试则会单独验证推断结果。
请求与跟踪查询
请求位于
crates/biome_module_graph/src/type_inference/requests/ 下。应为可复用的结果约定添加请求,而不是为单条 lint 规则添加请求。当输出和不确定性行为一致时,复用现有请求。
请求应定义稳定的元数据、唯一的规范执行路径、精确的源代码来源,以及明确的不确定性。通过 TypeInferenceRequestContext 组合操作;分析器代码不应构造底层查询输入。
只有当结果需要独立的记忆化和失效边界时,才添加跟踪查询。新查询必须:
- 返回使用方所需的最小语义结果;
- 接收数据库以及一个 Salsa 输入或内部化键;
- 只读取可能影响结果的依赖项;
- 定义缺失输入、歧义、循环和工作预算的处理方式;
- 保留
Unknown或Indeterminate,而不是臆造确定结果; - 使用当前的查询检测系列;
- 配备正确性测试和选择性执行测试。
对相同输入进行内部化会让它们共享标识;但这不会记忆化查询结果。有关当前的 trait、注册和测试要求,请参阅模块图指南中的 Request architecture 和 Adding a tracked query 部分。
解析与结果语义
按需解析会沿选定的查找路径进行,直到请求要求执行更广泛的工作。规范化会通过有界遍历解析可达句柄;它并不等同于完整模块推断。命名空间展开和受保护的深层导入回退可能大幅扩大工作范围,因此必须明确执行并使其可观察。
不要混淆以下结果:
| 结果 | 含义 |
|---|---|
None | 根据其文档约定,请求没有结果 |
InferredTypeData::Unknown | 推断得到了一个结构尚不确定的类型 |
InferredTypeData::UnknownKeyword | 源代码明确使用了 TypeScript 的 unknown 类型 |
TypeInferenceClassification::Indeterminate | 推断无法证明匹配或不匹配 |
TypeInferenceClassification::Match | 推断确切证明了条件成立 |
TypeInferenceClassification::NoMatch | 推断确切证明了条件不成立 |
未知或不确定的信息不代表否定结果。应保留这些信息,以避免误报诊断。更改这些路径前,请阅读规范中有关扩大范围、循环恢复和结果语义的部分。
ResolutionCtx 会在每个上下文中保留其循环集合、深度限制和缓存。解析过程中不要为另一个模块创建上下文并通过它递归。应改为经过跟踪查询、已用尽的导入或步骤预算,或者共享的声明求值器。模块图指南中的 Resolver helpers 部分列出了这些边界。
测试与性能分析
针对受影响范围最小的边界进行测试:
- 用于原始记录和引用的收集器测试;
- 用于结果、导入、循环和不确定性的请求/查询测试;
- 当依赖项或失效范围发生变化时,进行 Salsa 事件测试;
- 用于最终诊断行为的分析器 fixture。
对于针对性流程,证明无关编辑会复用查询,而已消费的输入会触发重新计算。除非请求契约或受测回退逻辑有此要求,否则应断言不会执行整个模块的推断。
当请求解析的数据超出预期时,请使用模块图指南中记录的维护配置文件。请求和查询的耗时均为包含下游操作的耗时,不得相加。快照机制请加载 testing-codegen。
审查清单
- 实现使用了正确的原始世界、推断世界或面向分析器的世界。
- 请求或查询是满足其契约的最小边界。
- 缺失数据、歧义、循环和预算耗尽均能保留不确定性。
- 解析过程中不会在没有受跟踪查询、已消耗预算或共享声明求值器约束的情况下创建
ResolutionCtx。 - 跨模块的推断数据仍归其源模块所有。
- 新的请求契约可复用,且元数据稳定。
- 新的查询键和依赖项是稳定的 Salsa 值。
- 不会执行整个模块的推断,或有明确理由说明为何需要执行。
- 正确性和选择性执行测试覆盖了变更的边界。