Skip to content

Enhancement: ESLint 10 crash: @typescript-eslint scope manager missing addGlobals (TypeError during SourceCode.finalize) #11830

@michaelgiraldo

Description

@michaelgiraldo

Before You File a Proposal Please Confirm You Have Done The Following...

Relevant Package

typescript-eslint

My proposal is suitable for this project

  • I believe my proposal would be useful to the broader TypeScript community (meaning it is not a niche proposal).

Description

Executive summary

ESLint 10 introduces a breaking change to scope management: scope managers must now implement an addGlobals(names: string[]) method.
This hook is invoked during source finalization to seed declared globals.

The current TypeScript scope manager in @typescript-eslint 8.49.x does not provide this method, which causes ESLint 10 to throw before any rules execute:

TypeError: scopeManager.addGlobals is not a function
    at addDeclaredGlobals (.../eslint/lib/languages/js/source-code/source-code.js:221:15)
    at SourceCode.finalize ...

As a result, TypeScript projects cannot run ESLint 10 at all, even with a trivial flat config.

The proposal is to implement addGlobals (or an equivalent compatibility shim) in the TypeScript scope manager to restore ESLint 10 compatibility.
This is a small, well-scoped change that unblocks early adopters, prevents downstream breakage, and aligns the TypeScript tooling ecosystem with ESLint’s new contract.


What the proposal does

  1. Implements the new scope manager API surface required by ESLint 10

    • Add an addGlobals(names: string[]) method to the TS scope manager used by @typescript-eslint/parser.
    • Ensure it creates global variables for the provided names and resolves references to them in the global scope.
  2. Removes a hard crash in the ESLint 10 execution path

    • ESLint 10 calls addGlobals during SourceCode.finalize().
      Without the method, linting crashes before rules run.
  3. Keeps behavior consistent with ESLint 10’s built-in scope manager

    • Aim for feature parity so rules and plugins see the symbol table they expect.
  4. Provides a defensive shim

    • For dual-support with ESLint < 10, the method can be a no-op when not invoked, preserving backward compatibility.

Why this is useful

  1. Restores linting for TypeScript users on ESLLint 10
    TypeScript projects cannot adopt ESLint 10 until this missing method is implemented.

  2. Prevents ecosystem breakage
    Many tools depend on @typescript-eslint/parser; missing APIs cause all ESLint-10-based TypeScript stacks to fail at startup.

  3. Aligns with ESLint’s documented breaking change
    ESLint 10 requires addGlobals for all scope managers.

  4. Improves developer experience and trust
    Early adopters and CI pipelines expect compatibility with major ESLint releases.

  5. Keeps rule authors and plugin authors unblocked
    A functioning scope graph is essential for custom rules and advanced analysis.

  6. Reduces support load
    Avoids duplicate crash reports tied to the missing method.


Technical context and expected behavior

  • What ESLint 10 expects:
    During SourceCode.finalize(), ESLint collects declared globals and calls:

    scopeManager.addGlobals(names: string[])
  • What’s missing:
    The TypeScript scope manager (8.49.x) does not implement the method.

  • Target behavior:
    Mirror ESLint’s default semantics:

    • Create or ensure variables exist in the global scope.
    • Resolve references to those globals.
    • Avoid duplication or unintended shadowing (idempotent behavior).

Backward/forward compatibility

  • Backward: ESLint < 10 never calls addGlobals, so adding the method is safe and non-breaking.
  • Forward: Implementing now prevents breakage in future 10.x releases and stabilizes the ecosystem.

Scope of change

  • Surface area: Only the TypeScript scope manager used by @typescript-eslint/parser.
  • Risk: Low, if implemented to match ESLint’s semantics.
  • Testing needs:
    • Creating globals for provided names.
    • Reference resolution correctness.
    • No duplication or unexpected behavior.

Why this should be prioritized

  • Hard blocker: ESLint 10 + TypeScript cannot run at all.
  • Ecosystem impact: Very high—@typescript-eslint is the standard parser for TS.
  • Clear fix: ESLint documents the required change; the implementation is straightforward.
  • Reduces churn: Fixing now avoids multiple duplicate issues as ESLint 10 adoption increases.

Proposed acceptance criteria

  • addGlobals(names: string[]) exists and matches ESLint 10’s expected behavior.
  • TypeScript projects can run eslint@10 end-to-end (no pre-rule crash).
  • Regression tests ensure global creation and reference resolution.
  • Optional: method is idempotent and guards against repeated invocations.

Summary

This proposal adds ESLint 10’s required addGlobals hook to the @typescript-eslint scope manager.
It is a focused, low-risk update that:

  • Unblocks TypeScript users on ESLint 10
  • Aligns the parser with documented ESLint 10 requirements
  • Eliminates a startup crash that currently prevents linting
  • Reduces support burden and keeps the ecosystem stable

Implementing this ensures that the TypeScript ESLint toolchain remains compatible, reliable, and ready for ESLint 10 adoption.

Additional Info

Additional Technical Details

  • Crash context and stack:
    The failure occurs before any rule evaluation, during ESLint’s source‑finalization step. ESLint 10 invokes addDeclaredGlobals inside
    eslint/lib/languages/js/source-code/source-code.js (around line 221), which then attempts to call scopeManager.addGlobals.
    Because the TypeScript scope manager (from @typescript-eslint/parser 8.49.x) does not implement this method, the runtime throws:

    TypeError: scopeManager.addGlobals is not a function
    

    Stack traces consistently begin at SourceCode.finalize, flow through addDeclaredGlobals, and abort the run.
    No rules execute, and no diagnostics are emitted.

  • Dependency setup and prior blockers:
    Initially, the flat config required globals, and ESLint 10 would fail earlier if the package was not installed.
    Installing globals resolves the import issue—but the addGlobals crash remains, confirming that the only blocker is the missing method on the TS scope manager.
    No custom rules or additional plugins beyond @typescript-eslint were used in the repro.

  • Reproduction fidelity:
    The minimal repro uses:

    • a flat config,
    • the TypeScript parser,
    • a single TypeScript file (const x: number = 1;).

    parserOptions.project was set to false to avoid project lookup; the crash still occurs.
    Providing a tsconfig does not affect the behavior—the crash happens before project-related operations.

    The crash reproduces on:

    • Node 20.19.0
    • Node 25.2.1

    Every run of:

    npx eslint index.ts --max-warnings=0
    

    with [email protected] and @typescript-eslint 8.49.0 fails identically.

  • Regression confirmation:
    Using the same config and file, ESLint 9.39.1 runs successfully and reports no errors.
    This isolates the regression to ESLint 10’s updated scope‑manager contract and confirms that the TypeScript parser/plugin continue functioning correctly under ESLint 9.

  • Alignment with ESLint 10 breaking changes:
    ESLint 10’s release notes explicitly state that custom scope managers must implement:

    addGlobals(names: string[])

    and are required to resolve references for declared globals.
    The observed crash is exactly what occurs when that API is missing.
    Implementing addGlobals in the TS scope manager should eliminate the failure and restore intended behavior.

  • Scope and risk considerations:
    The required change is narrowly scoped to the TS scope manager implementation.
    Expected behavior: create or ensure globals exist and resolve references appropriately.
    A robust version should:

    • be idempotent,
    • avoid duplication,
    • work safely with ESLint 9.x (which never calls addGlobals).

    Tests should verify:

    • globals are created,
    • references resolve to the global scope,
    • existing bindings are not shadowed or corrupted.
  • Downstream impact:
    Since @typescript-eslint/parser is the de facto parser for nearly all TypeScript tooling (CLIs, build systems, editors, CI),
    the absence of addGlobals means every attempt to adopt ESLint 10 in TS codebases fails immediately.
    Adding the method unblocks early adopters, reduces duplicate crash reports, and preserves confidence in the TS toolchain’s alignment with ESLint majors.

  • Mitigation in the interim:
    Projects may pin ESLint to 9.39.1 to avoid the crash, but the correct long‑term fix is implementing addGlobals in the TS scope manager to restore ESLint 10 compatibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttriageWaiting for team members to take a look

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions