type-inference
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePurpose
用途
Use this skill when working with Biome's type inference system and module graph. Covers type references, resolution phases, and the architecture designed for IDE performance.
当你需要处理Biome的类型推断系统和模块图时,可以参考本指南。内容涵盖类型引用、解析阶段,以及为IDE性能优化设计的架构。
Prerequisites
前提条件
- Read for architecture details
crates/biome_js_type_info/CONTRIBUTING.md - Understand Biome's focus on IDE support and instant updates
- Familiarity with TypeScript type system concepts
- 阅读以了解架构细节
crates/biome_js_type_info/CONTRIBUTING.md - 理解Biome对IDE支持和即时更新的重视
- 熟悉TypeScript类型系统的相关概念
Key Concepts
核心概念
Module Graph Constraint
模块图约束
Critical rule: No module may copy or clone data from another module, not even behind .
ArcWhy: Any module can be updated at any time (IDE file changes). Copying data would create stale references that are hard to invalidate.
Solution: Use instead of direct type references.
TypeReference核心规则:任何模块都不得复制或克隆其他模块的数据,即使通过也不行。
Arc原因:任何模块都可能随时更新(比如IDE中的文件变更)。复制数据会导致难以失效的陈旧引用。
解决方案:使用而非直接的类型引用。
TypeReferenceType Data Structure
类型数据结构
Types are stored in enum with many variants:
TypeDatarust
enum TypeData {
Unknown, // Inference not implemented
UnknownKeyword, // Explicit 'unknown' keyword
String, // String type
Number, // Number type
Function(FunctionType), // Function with parameters
Object(ObjectType), // Object with properties
Reference, // Reference to another type
// ... many more variants
}类型存储在包含多种变体的枚举中:
TypeDatarust
enum TypeData {
Unknown, // Inference not implemented
UnknownKeyword, // Explicit 'unknown' keyword
String, // String type
Number, // Number type
Function(FunctionType), // Function with parameters
Object(ObjectType), // Object with properties
Reference, // Reference to another type
// ... many more variants
}Type References
类型引用
Instead of direct type references, use :
TypeReferencerust
enum TypeReference {
Qualifier(TypeReferenceQualifier), // Name-based reference
Resolved(ResolvedTypeId), // Resolved to type ID
Import(TypeImportQualifier), // Import reference
Unknown, // Could not resolve
}不要使用直接的类型引用,而是使用:
TypeReferencerust
enum TypeReference {
Qualifier(TypeReferenceQualifier), // Name-based reference
Resolved(ResolvedTypeId), // Resolved to type ID
Import(TypeImportQualifier), // Import reference
Unknown, // Could not resolve
}Type Resolution Phases
类型解析阶段
1. Local Inference
1. 本地推断
What: Derives types from expressions without surrounding context.
Example: For , creates:
a + brust
TypeData::TypeofExpression(TypeofExpression::Addition {
left: TypeReference::from(TypeReferenceQualifier::from_name("a")),
right: TypeReference::from(TypeReferenceQualifier::from_name("b"))
})Where: Implemented in
local_inference.rsOutput: Types with unresolved references
TypeReference::Qualifier内容:在不考虑上下文的情况下,从表达式推导类型。
示例:对于,会生成:
a + brust
TypeData::TypeofExpression(TypeofExpression::Addition {
left: TypeReference::from(TypeReferenceQualifier::from_name("a")),
right: TypeReference::from(TypeReferenceQualifier::from_name("b"))
})实现位置:
local_inference.rs输出:包含未解析引用的类型
TypeReference::Qualifier2. Module-Level ("Thin") Inference
2. 模块级("轻量")推断
What: Resolves references within a single module's scope.
Process:
- Takes results from local inference
- Looks up qualifiers in local scopes
- Converts to if found locally
TypeReference::Resolved - Converts to if from import statement
TypeReference::Import - Falls back to globals (like ,
Array)Promise - Uses if nothing found
TypeReference::Unknown
Where: Implemented in
js_module_info/collector.rsOutput: Types with resolved local references, import markers, or unknown
内容:解析单个模块作用域内的引用。
流程:
- 接收本地推断的结果
- 在本地作用域中查找限定符
- 如果在本地找到,转换为
TypeReference::Resolved - 如果来自导入语句,转换为
TypeReference::Import - 回退到全局类型(如、
Array)Promise - 如果未找到任何匹配,使用
TypeReference::Unknown
实现位置:
js_module_info/collector.rs输出:包含已解析本地引用、导入标记或未知类型的结果
3. Full Inference
3. 完整推断
What: Resolves import references across module boundaries.
Process:
- Has access to entire module graph
- Resolves by following imports
TypeReference::Import - Converts to after following imports
TypeReference::Resolved
Where: Implemented in
js_module_info/scoped_resolver.rsLimitation: Results cannot be cached (would become stale on file changes)
内容:跨模块边界解析导入引用。
流程:
- 可访问整个模块图
- 通过追踪导入关系解析
TypeReference::Import - 追踪完成后转换为
TypeReference::Resolved
实现位置:
js_module_info/scoped_resolver.rs限制:结果无法缓存(文件变更会导致结果失效)
Working with Type Resolvers
类型解析器使用指南
Available Resolvers
可用的解析器
rust
// 1. For tests
HardcodedSymbolResolver
// 2. For globals (Array, Promise, etc.)
GlobalsResolver
// 3. For thin inference (single module)
JsModuleInfoCollector
// 4. For full inference (across modules)
ModuleResolverrust
// 1. 用于测试
HardcodedSymbolResolver
// 2. 用于全局类型(Array、Promise等)
GlobalsResolver
// 3. 用于轻量推断(单模块)
JsModuleInfoCollector
// 4. 用于完整推断(跨模块)
ModuleResolverUsing a Resolver
使用解析器
rust
use biome_js_type_info::{TypeResolver, ResolvedTypeData};
fn analyze_type(resolver: &impl TypeResolver, type_ref: TypeReference) {
// Resolve the reference
let resolved_data: ResolvedTypeData = resolver.resolve_type(type_ref);
// Get raw data for pattern matching
match resolved_data.as_raw_data() {
TypeData::String => { /* handle string */ },
TypeData::Number => { /* handle number */ },
TypeData::Function(func) => { /* handle function */ },
_ => { /* handle others */ }
}
// Resolve nested references
if let TypeData::Reference(inner_ref) = resolved_data.as_raw_data() {
let inner_data = resolver.resolve_type(*inner_ref);
// Process inner type
}
}rust
use biome_js_type_info::{TypeResolver, ResolvedTypeData};
fn analyze_type(resolver: &impl TypeResolver, type_ref: TypeReference) {
// Resolve the reference
let resolved_data: ResolvedTypeData = resolver.resolve_type(type_ref);
// Get raw data for pattern matching
match resolved_data.as_raw_data() {
TypeData::String => { /* handle string */ },
TypeData::Number => { /* handle number */ },
TypeData::Function(func) => { /* handle function */ },
_ => { /* handle others */ }
}
// Resolve nested references
if let TypeData::Reference(inner_ref) = resolved_data.as_raw_data() {
let inner_data = resolver.resolve_type(*inner_ref);
// Process inner type
}
}Type Flattening
类型扁平化
What: Converts complex type expressions to concrete types.
Example: After resolving :
a + b- If both are → Flatten to
TypeData::NumberTypeData::Number - Otherwise → Usually flatten to
TypeData::String
Where: Implemented in
flattening.rs内容:将复杂的类型表达式转换为具体类型。
示例:解析后:
a + b- 如果两者都是→ 扁平化为
TypeData::NumberTypeData::Number - 否则 → 通常扁平化为
TypeData::String
实现位置:
flattening.rsCommon Workflows
常见工作流程
Implement Type-Aware Lint Rule
实现类型感知的Lint规则
rust
use biome_analyze::Semantic;
use biome_js_type_info::{TypeResolver, TypeData};
impl Rule for MyTypeRule {
type Query = Semantic<JsCallExpression>;
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();
let model = ctx.model();
// Get type resolver from model
let resolver = model.type_resolver();
// Get type of expression
let expr_type = node.callee().ok()?.infer_type(resolver);
// Check the type
match expr_type.as_raw_data() {
TypeData::Function(_) => { /* valid */ },
TypeData::Unknown => { /* might be valid, can't tell */ },
_ => { return Some(()); /* not callable */ }
}
None
}
}rust
use biome_analyze::Semantic;
use biome_js_type_info::{TypeResolver, TypeData};
impl Rule for MyTypeRule {
type Query = Semantic<JsCallExpression>;
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();
let model = ctx.model();
// Get type resolver from model
let resolver = model.type_resolver();
// Get type of expression
let expr_type = node.callee().ok()?.infer_type(resolver);
// Check the type
match expr_type.as_raw_data() {
TypeData::Function(_) => { /* valid */ },
TypeData::Unknown => { /* might be valid, can't tell */ },
_ => { return Some(()); /* not callable */ }
}
None
}
}Navigate Type References
遍历类型引用
rust
fn is_array_type(resolver: &impl TypeResolver, type_ref: TypeReference) -> bool {
let resolved = resolver.resolve_type(type_ref);
// Follow references
let data = match resolved.as_raw_data() {
TypeData::Reference(ref_to) => resolver.resolve_type(*ref_to),
other => resolved,
};
// Check if it's an Array
matches!(data.as_raw_data(), TypeData::Array(_))
}rust
fn is_array_type(resolver: &impl TypeResolver, type_ref: TypeReference) -> bool {
let resolved = resolver.resolve_type(type_ref);
// Follow references
let data = match resolved.as_raw_data() {
TypeData::Reference(ref_to) => resolver.resolve_type(*ref_to),
other => resolved,
};
// Check if it's an Array
matches!(data.as_raw_data(), TypeData::Array(_))
}Work with Function Types
处理函数类型
rust
fn analyze_function(resolver: &impl TypeResolver, type_ref: TypeReference) {
let resolved = resolver.resolve_type(type_ref);
if let TypeData::Function(func_type) = resolved.as_raw_data() {
// Access parameters
for param in func_type.parameters() {
let param_type = resolver.resolve_type(param.type_ref());
// Analyze parameter type
}
// Access return type
let return_type = resolver.resolve_type(func_type.return_type());
}
}rust
fn analyze_function(resolver: &impl TypeResolver, type_ref: TypeReference) {
let resolved = resolver.resolve_type(type_ref);
if let TypeData::Function(func_type) = resolved.as_raw_data() {
// Access parameters
for param in func_type.parameters() {
let param_type = resolver.resolve_type(param.type_ref());
// Analyze parameter type
}
// Access return type
let return_type = resolver.resolve_type(func_type.return_type());
}
}Architecture Principles
架构原则
Why Type References?
为什么使用类型引用?
Advantages:
- No stale data: Module updates don't leave old types in memory
- Better performance: Types stored in vectors (data locality)
- Easier debugging: Can inspect all types in vector
- Simpler algorithms: Process vectors instead of traversing graphs
Trade-off: Must explicitly resolve references (not automatic like )
Arc优势:
- 无陈旧数据:模块更新不会在内存中留下旧类型
- 性能更优:类型存储在向量中(数据局部性好)
- 调试更易:可以检查向量中的所有类型
- 算法更简单:处理向量而非遍历图结构
权衡:必须显式解析引用(不像那样自动)
ArcResolvedTypeId Structure
ResolvedTypeId结构
rust
struct ResolvedTypeId(ResolverId, TypeId)- (u32): Index into a type vector
TypeId - (u32): Identifies which vector to use
ResolverId - Total: 64 bits (compact representation)
rust
struct ResolvedTypeId(ResolverId, TypeId)- (u32):类型向量的索引
TypeId - (u32):标识要使用的向量
ResolverId - 总大小:64位(紧凑表示)
ResolvedTypeData
ResolvedTypeData
Always work with from resolver, not raw :
ResolvedTypeData&TypeDatarust
// Good - tracks resolver context
let resolved_data: ResolvedTypeData = resolver.resolve_type(type_ref);
// Be careful - loses resolver context
let raw_data: &TypeData = resolved_data.as_raw_data();
// Can't resolve nested TypeReferences without ResolverId!始终使用解析器返回的,而非原始的:
ResolvedTypeData&TypeDatarust
// 推荐 - 追踪解析器上下文
let resolved_data: ResolvedTypeData = resolver.resolve_type(type_ref);
// 注意 - 丢失了解析器上下文
let raw_data: &TypeData = resolved_data.as_raw_data();
// 没有ResolverId就无法解析嵌套的TypeReference!Tips
提示
- Unknown types: means inference not implemented, treat as "could be anything"
TypeData::Unknown - Follow references: Always follow to get actual type
TypeData::Reference - Resolver context: Keep when possible, don't extract raw
ResolvedTypeDataearlyTypeData - Performance: Type vectors are fast - iterate directly instead of recursive traversal
- IDE focus: All design decisions prioritize instant IDE updates over CLI performance
- No caching: Full inference results can't be cached (would become stale)
- Globals: Currently hardcoded, eventually should use TypeScript's files
.d.ts
- 未知类型:表示尚未实现该类型的推断,应视为“可能是任意类型”
TypeData::Unknown - 追踪引用:始终要追踪以获取实际类型
TypeData::Reference - 解析器上下文:尽可能保留,不要过早提取原始
ResolvedTypeDataTypeData - 性能:类型向量的访问速度快,应直接迭代而非递归遍历
- IDE优先:所有设计决策都优先考虑IDE的即时更新,而非CLI性能
- 禁止缓存:完整推断的结果无法缓存(会因文件变更失效)
- 全局类型:目前是硬编码实现,最终应使用TypeScript的文件
.d.ts
Common Patterns
常见模式
rust
// Pattern 1: Resolve and flatten
let type_ref = expr.infer_type(resolver);
let flattened = type_ref.flatten(resolver);
// Pattern 2: Check if type matches
fn is_string_type(resolver: &impl TypeResolver, type_ref: TypeReference) -> bool {
let resolved = resolver.resolve_type(type_ref);
matches!(resolved.as_raw_data(), TypeData::String)
}
// Pattern 3: Handle unknown gracefully
match resolved.as_raw_data() {
TypeData::Unknown | TypeData::UnknownKeyword => {
// Can't verify, assume valid
return None;
}
TypeData::String => { /* handle */ }
_ => { /* handle */ }
}rust
// Pattern 1: Resolve and flatten
let type_ref = expr.infer_type(resolver);
let flattened = type_ref.flatten(resolver);
// Pattern 2: Check if type matches
fn is_string_type(resolver: &impl TypeResolver, type_ref: TypeReference) -> bool {
let resolved = resolver.resolve_type(type_ref);
matches!(resolved.as_raw_data(), TypeData::String)
}
// Pattern 3: Handle unknown gracefully
match resolved.as_raw_data() {
TypeData::Unknown | TypeData::UnknownKeyword => {
// Can't verify, assume valid
return None;
}
TypeData::String => { /* handle */ }
_ => { /* handle */ }
}References
参考资料
- Architecture guide:
crates/biome_js_type_info/CONTRIBUTING.md - Module graph:
crates/biome_module_graph/ - Type resolver trait:
crates/biome_js_type_info/src/resolver.rs - Flattening:
crates/biome_js_type_info/src/flattening.rs
- 架构指南:
crates/biome_js_type_info/CONTRIBUTING.md - 模块图:
crates/biome_module_graph/ - 类型解析器 trait:
crates/biome_js_type_info/src/resolver.rs - 类型扁平化:
crates/biome_js_type_info/src/flattening.rs