type-inference

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Purpose

用途

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

前提条件

  1. Read
    crates/biome_js_type_info/CONTRIBUTING.md
    for architecture details
  2. Understand Biome's focus on IDE support and instant updates
  3. Familiarity with TypeScript type system concepts
  1. 阅读
    crates/biome_js_type_info/CONTRIBUTING.md
    以了解架构细节
  2. 理解Biome对IDE支持和即时更新的重视
  3. 熟悉TypeScript类型系统的相关概念

Key Concepts

核心概念

Module Graph Constraint

模块图约束

Critical rule: No module may copy or clone data from another module, not even behind
Arc
.
Why: Any module can be updated at any time (IDE file changes). Copying data would create stale references that are hard to invalidate.
Solution: Use
TypeReference
instead of direct type references.
核心规则:任何模块都不得复制或克隆其他模块的数据,即使通过
Arc
也不行。
原因:任何模块都可能随时更新(比如IDE中的文件变更)。复制数据会导致难以失效的陈旧引用。
解决方案:使用
TypeReference
而非直接的类型引用。

Type Data Structure

类型数据结构

Types are stored in
TypeData
enum with many variants:
rust
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
}
类型存储在包含多种变体的
TypeData
枚举中:
rust
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
TypeReference
:
rust
enum TypeReference {
    Qualifier(TypeReferenceQualifier),  // Name-based reference
    Resolved(ResolvedTypeId),            // Resolved to type ID
    Import(TypeImportQualifier),         // Import reference
    Unknown,                             // Could not resolve
}
不要使用直接的类型引用,而是使用
TypeReference
rust
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
a + b
, creates:
rust
TypeData::TypeofExpression(TypeofExpression::Addition {
    left: TypeReference::from(TypeReferenceQualifier::from_name("a")),
    right: TypeReference::from(TypeReferenceQualifier::from_name("b"))
})
Where: Implemented in
local_inference.rs
Output: Types with unresolved
TypeReference::Qualifier
references
内容:在不考虑上下文的情况下,从表达式推导类型。
示例:对于
a + b
,会生成:
rust
TypeData::TypeofExpression(TypeofExpression::Addition {
    left: TypeReference::from(TypeReferenceQualifier::from_name("a")),
    right: TypeReference::from(TypeReferenceQualifier::from_name("b"))
})
实现位置
local_inference.rs
输出:包含未解析
TypeReference::Qualifier
引用的类型

2. Module-Level ("Thin") Inference

2. 模块级("轻量")推断

What: Resolves references within a single module's scope.
Process:
  1. Takes results from local inference
  2. Looks up qualifiers in local scopes
  3. Converts to
    TypeReference::Resolved
    if found locally
  4. Converts to
    TypeReference::Import
    if from import statement
  5. Falls back to globals (like
    Array
    ,
    Promise
    )
  6. Uses
    TypeReference::Unknown
    if nothing found
Where: Implemented in
js_module_info/collector.rs
Output: Types with resolved local references, import markers, or unknown
内容:解析单个模块作用域内的引用。
流程
  1. 接收本地推断的结果
  2. 在本地作用域中查找限定符
  3. 如果在本地找到,转换为
    TypeReference::Resolved
  4. 如果来自导入语句,转换为
    TypeReference::Import
  5. 回退到全局类型(如
    Array
    Promise
  6. 如果未找到任何匹配,使用
    TypeReference::Unknown
实现位置
js_module_info/collector.rs
输出:包含已解析本地引用、导入标记或未知类型的结果

3. Full Inference

3. 完整推断

What: Resolves import references across module boundaries.
Process:
  1. Has access to entire module graph
  2. Resolves
    TypeReference::Import
    by following imports
  3. Converts to
    TypeReference::Resolved
    after following imports
Where: Implemented in
js_module_info/scoped_resolver.rs
Limitation: Results cannot be cached (would become stale on file changes)
内容:跨模块边界解析导入引用。
流程
  1. 可访问整个模块图
  2. 通过追踪导入关系解析
    TypeReference::Import
  3. 追踪完成后转换为
    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)
ModuleResolver
rust
// 1. 用于测试
HardcodedSymbolResolver

// 2. 用于全局类型(Array、Promise等)
GlobalsResolver

// 3. 用于轻量推断(单模块)
JsModuleInfoCollector

// 4. 用于完整推断(跨模块)
ModuleResolver

Using 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
    TypeData::Number
    → Flatten to
    TypeData::Number
  • Otherwise → Usually flatten to
    TypeData::String
Where: Implemented in
flattening.rs
内容:将复杂的类型表达式转换为具体类型。
示例:解析
a + b
后:
  • 如果两者都是
    TypeData::Number
    → 扁平化为
    TypeData::Number
  • 否则 → 通常扁平化为
    TypeData::String
实现位置
flattening.rs

Common 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:
  1. No stale data: Module updates don't leave old types in memory
  2. Better performance: Types stored in vectors (data locality)
  3. Easier debugging: Can inspect all types in vector
  4. Simpler algorithms: Process vectors instead of traversing graphs
Trade-off: Must explicitly resolve references (not automatic like
Arc
)
优势
  1. 无陈旧数据:模块更新不会在内存中留下旧类型
  2. 性能更优:类型存储在向量中(数据局部性好)
  3. 调试更易:可以检查向量中的所有类型
  4. 算法更简单:处理向量而非遍历图结构
权衡:必须显式解析引用(不像
Arc
那样自动)

ResolvedTypeId Structure

ResolvedTypeId结构

rust
struct ResolvedTypeId(ResolverId, TypeId)
  • TypeId
    (u32): Index into a type vector
  • ResolverId
    (u32): Identifies which vector to use
  • Total: 64 bits (compact representation)
rust
struct ResolvedTypeId(ResolverId, TypeId)
  • TypeId
    (u32):类型向量的索引
  • ResolverId
    (u32):标识要使用的向量
  • 总大小:64位(紧凑表示)

ResolvedTypeData

ResolvedTypeData

Always work with
ResolvedTypeData
from resolver, not raw
&TypeData
:
rust
// 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
,而非原始的
&TypeData
rust
// 推荐 - 追踪解析器上下文
let resolved_data: ResolvedTypeData = resolver.resolve_type(type_ref);

// 注意 - 丢失了解析器上下文
let raw_data: &TypeData = resolved_data.as_raw_data();
// 没有ResolverId就无法解析嵌套的TypeReference!

Tips

提示

  • Unknown types:
    TypeData::Unknown
    means inference not implemented, treat as "could be anything"
  • Follow references: Always follow
    TypeData::Reference
    to get actual type
  • Resolver context: Keep
    ResolvedTypeData
    when possible, don't extract raw
    TypeData
    early
  • 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
    .d.ts
    files
  • 未知类型
    TypeData::Unknown
    表示尚未实现该类型的推断,应视为“可能是任意类型”
  • 追踪引用:始终要追踪
    TypeData::Reference
    以获取实际类型
  • 解析器上下文:尽可能保留
    ResolvedTypeData
    ,不要过早提取原始
    TypeData
  • 性能:类型向量的访问速度快,应直接迭代而非递归遍历
  • 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