Skip to content
Development
Skill

/type-inference

Guide for working with Biome's module graph and type inference system. Use when implementing type-aware lint rules, understanding type resolution, working on the module graph infrastructure, or implementing type inference for new features.

From plugin
biome
26k12 skills
Install
$ npx -y skills add biomejs/biome --skill type-inference --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/type-inference

Context preview

The summary Claude sees to decide when to auto-load this skill.

Guide for working with Biome's module graph and type inference system. Use when implementing type-aware lint rules, understanding type resolution, working on the module graph infrastructure, or implementing type inference for new features.

SKILL.md

type-inference.SKILL.md
name: type-inference
description: Guide for working with Biome's module graph and type inference system. Use when implementing type-aware lint rules, understanding type resolution, working on the module graph infrastructure, or implementing type inference for new features.
compatibility: Designed for coding agents working on the Biome codebase (github.com/biomejs/biome).

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.

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

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.

Type Data Structure

Types are stored in `TypeData` enum with many variants:

// Simplified — see crates/biome_js_type_info/src/type_data.rs for the full enum
enum TypeData {
    Unknown,                            // Inference not implemented
    Global,                             // Global type reference
    BigInt, Boolean, Null, Number,      // Primitive types
    String, Symbol, Undefined,
    Function(Box<Function>),            // Function with parameters
    Object(Box<Object>),                // Object with properties
    Class(Box<Class>),                  // Class definition
    Interface(Box<Interface>),          // Interface definition
    Union(Box<Union>),                  // Union type (A | B)
    Intersection(Box<Intersection>),    // Intersection type (A & B)
    Tuple(Box<Tuple>),                  // Tuple type
    Literal(Box<Literal>),              // Literal type ("foo", 42)
    Reference(TypeReference),           // Reference to another type
    TypeofExpression(Box<TypeofExpression>), // typeof an expression
    // ... plus Conditional, Generic, TypeOperator, InstanceOf,
    //     keyword variants (AnyKeyword, NeverKeyword, VoidKeyword, etc.)
}

Type References

Instead of direct type references, use `TypeReference`:

enum TypeReference {
    Qualifier(Box<TypeReferenceQualifier>),  // Name-based reference
    Resolved(ResolvedTypeId),                 // Resolved to type ID
    Import(Box<TypeImportQualifier>),         // Import reference
}

**Note:** There is no `Unknown` variant. Unknown types are represented as `TypeReference::Resolved(GLOBAL_UNKNOWN_ID)`. Use `TypeReference::unknown()` to create one.

Type Resolution Phases

1. Local Inference

**What**: Derives types from expressions without surrounding context.

**Example**: For `a + b`, creates:

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

2. Module-Level ("Thin") Inference

**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 is found

**Where**: Implemented in `js_module_info/collector.rs`

**Output**: Types with resolved local references, import markers, or unknown

3. Full Inference

**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**: The Salsa-backed implementation starts at `db/queries/type_inference.rs::infer_module_types` and uses helpers under `db/type_inference/`. `js_module_info/module_resolver.rs` contains the legacy `TypeResolver`-based path.

**Caching**: `infer_module_types` is tracked by Salsa. Imported module results are dependencies, so Salsa invalidates affected importers after a change.

Working with Type Resolvers

Available Resolvers

// 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

Using a Resolver

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`

Common Workflows

Implement Type-Aware Lint Rule

use biome_analyze::Semantic;
use biome_js_type_info::{TypeResolver, T
Read more
Ships withbiome

A toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.

Get the whole plugin
Stats
25,543
Stars
1,180
Forks
Active
Maintenance
Rust
Language
Apache-2.0
License
1h ago
Last commit
3y ago
Created

Repo: biomejs/biome

Other skills on biome.