Skip to content

fix(noUnusedVariables): regression in previous refactor#9520

Merged
ematipico merged 1 commit intomainfrom
fix/ambient-promotion
Mar 17, 2026
Merged

fix(noUnusedVariables): regression in previous refactor#9520
ematipico merged 1 commit intomainfrom
fix/ambient-promotion

Conversation

@ematipico
Copy link
Member

@ematipico ematipico commented Mar 17, 2026

Summary

Closes #9519

Used AI to add new tests to cover missing cases.

Test Plan

Added new tests

Docs

@changeset-bot
Copy link

changeset-bot bot commented Mar 17, 2026

🦋 Changeset detected

Latest commit: 1b05a3c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Mar 17, 2026
@ematipico ematipico force-pushed the fix/ambient-promotion branch from cf6874d to 6c9e8d3 Compare March 17, 2026 10:06
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Walkthrough

This PR fixes the noUnusedVariables rule so variables referenced only within typeof type expressions are no longer reported as unused. It adds a TypeScript test exercising multiple typeof-related scenarios and refines semantic scope-pop logic to only promote AmbientRead when a corresponding imported (dual) binding exists, narrowing previous promotion behaviour. No public API or exported declarations were changed.

Possibly related PRs

Suggested reviewers

  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: fixing a regression in the noUnusedVariables rule from a previous refactor.
Description check ✅ Passed The description is related to the changeset, referencing the closed issue #9519 and explaining that AI was used to add tests for missing cases.
Linked Issues check ✅ Passed The PR addresses all requirements from issue #9519: fixes the noUnusedVariables regression for typeof type expressions, adds comprehensive test coverage in validTypeof.ts, and corrects the ambient read promotion logic in events.rs.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the noUnusedVariables regression: the test file covers typeof usage patterns, the changelog entry documents the fix, and events.rs corrects the specific ambient read promotion issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ambient-promotion
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

@github-actions
Copy link
Contributor

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 53139 53139 0
Passed 51919 51919 0
Failed 1178 1178 0
Panics 42 42 0
Coverage 97.70% 97.70% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 38 38 0
Passed 37 37 0
Failed 1 1 0
Panics 0 0 0
Coverage 97.37% 97.37% 0.00%

markdown/commonmark

Test result main count This PR count Difference
Total 652 652 0
Passed 652 652 0
Failed 0 0 0
Panics 0 0 0
Coverage 100.00% 100.00% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 5466 5466 0
Passed 1915 1915 0
Failed 3551 3551 0
Panics 0 0 0
Coverage 35.03% 35.03% 0.00%

ts/babel

Test result main count This PR count Difference
Total 636 636 0
Passed 568 568 0
Failed 68 68 0
Panics 0 0 0
Coverage 89.31% 89.31% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18875 18875 0
Passed 13014 13014 0
Failed 5860 5860 0
Panics 1 1 0
Coverage 68.95% 68.95% 0.00%

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 17, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 156 skipped benchmarks1


Comparing fix/ambient-promotion (1b05a3c) with main (60cf024)

Open in CodSpeed

Footnotes

  1. 156 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@ematipico ematipico marked this pull request as draft March 17, 2026 10:32
@ematipico ematipico force-pushed the fix/ambient-promotion branch from 6c9e8d3 to 1b05a3c Compare March 17, 2026 10:48
@ematipico ematipico marked this pull request as ready for review March 17, 2026 10:48
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/public-spiders-wash.md:
- Line 5: Update the changeset line to follow the required bug-fix sentence
format by adding a colon immediately after the issue link and a brief
description — replace "Fixed
[`#9519`](https://github.com/biomejs/biome/issues/9519)." with "Fixed
[`#9519`](https://github.com/biomejs/biome/issues/9519): Now `noUnusedVariables`
doesn't flag variables that are used as `typeof` type." so the file
.changeset/public-spiders-wash.md conforms to the "Fixed [`#NUMBER`](issue link):
..." guideline.

In
`@crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validTypeof.ts`:
- Around line 26-34: The tests use type annotations that reference variables
declared inside function bodies (return type typeof result in example3 and
parameter type typeof defaults in example4), which is invalid; move the
declarations for result and defaults out of their respective functions so the
types refer to variables in the declaration scope (e.g., declare const result =
... and const defaults = ... at module scope) and then update example3 and
example4 to use typeof result and typeof defaults respectively.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b4cc7006-8d32-461f-b0c9-5c4d6132c5da

📥 Commits

Reviewing files that changed from the base of the PR and between 6c9e8d3 and 1b05a3c.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validTypeof.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • .changeset/public-spiders-wash.md
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validTypeof.ts
  • crates/biome_js_semantic/src/events.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_semantic/src/events.rs

"@biomejs/biome": patch
---

Fixed [#9519](https://github.com/biomejs/biome/issues/9519). Now `noUnusedVariables` doesn't flag variables that are used as `typeof` type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the required bug-fix sentence format.

Line 5 should use Fixed [#9519](...): ... (with the colon immediately after the issue link).

Suggested edit
-Fixed [`#9519`](https://github.com/biomejs/biome/issues/9519). Now `noUnusedVariables` doesn't flag variables that are used as `typeof` type.
+Fixed [`#9519`](https://github.com/biomejs/biome/issues/9519): `noUnusedVariables` no longer flags variables used in `typeof` type expressions.

As per coding guidelines, "For bug fix changesets, start with 'Fixed [#NUMBER](issue link): ...' format".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Fixed [#9519](https://github.com/biomejs/biome/issues/9519). Now `noUnusedVariables` doesn't flag variables that are used as `typeof` type.
Fixed [`#9519`](https://github.com/biomejs/biome/issues/9519): `noUnusedVariables` no longer flags variables used in `typeof` type expressions.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~5-~5: Possible missing comma found.
Context: .../github.com//issues/9519). Now noUnusedVariables doesn't flag variab...

(AI_HYDRA_LEO_MISSING_COMMA)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/public-spiders-wash.md at line 5, Update the changeset line to
follow the required bug-fix sentence format by adding a colon immediately after
the issue link and a brief description — replace "Fixed
[`#9519`](https://github.com/biomejs/biome/issues/9519)." with "Fixed
[`#9519`](https://github.com/biomejs/biome/issues/9519): Now `noUnusedVariables`
doesn't flag variables that are used as `typeof` type." so the file
.changeset/public-spiders-wash.md conforms to the "Fixed [`#NUMBER`](issue link):
..." guideline.

Comment on lines +26 to +34
export function example3(): typeof result {
const result = compute();
return result;
}

// Variable used in typeof in a parameter type
export function example4(callback: (val: typeof defaults) => void) {
const defaults = { a: 1, b: 2 };
callback(defaults);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In TypeScript, is this valid: function f(): typeof x { const x = 1; return x; }?

💡 Result:

No. In function f(): typeof x { const x = 1; return x; }, the return type annotation is checked in the function’s declaration scope, and x is not in scope there (the const x is only in the function body), so you’ll get TS2304: Cannot find name 'x'.

TypeScript’s type query typeof can only refer to an identifier/property-access expression that’s actually in scope at the location of the type annotation [1], and identifier lookup is scope-based at the reference location [2].

Valid alternatives:

const x = 1;
function f(): typeof x { return x; }  // ok

or just let TS infer it:

function f() { const x = 1; return x; } // return type is 1

Sources:
[1] https://mariusschulz.com/blog/type-queries-and-typeof-in-typescript
[2] https://bartvds.github.io/tsspec-html/0.9.1/dist/expressions.html


🌐 Web query:

In TypeScript, can a function parameter type annotation reference a constdeclared inside the same function body (e.g.,function g(cb: (v: typeof y) => void) { const y = 1; })?

💡 Result:

No. The parameter list is outside the function body’s block scope, so a const declared inside the body isn’t in scope for a parameter type annotation. With your example, typeof y in cb: (v: typeof y) => void fails name resolution (you’ll get an error like “Cannot find name 'y'”). This follows from const having the same block/lexical scoping rules as let (only usable within/after its declaration in its block) and from the fact that parameter-list constructs can’t safely depend on locals from the body. [1], [2], [3]

Workarounds: move the value (or a type alias) outside the function body, or make it a parameter / generic type parameter instead. [2]

Sources:
[1] TypeScript 1.4 release notes (let block scoping / “cannot refer before its declaration”) (typescriptlang.org)
[2] TypeScript Handbook: Variable Declarations (const has same scoping rules as let) (typescriptlang.org)
[3] TypeScript Language Specification (parameter initializer restrictions / parameter list vs body) (sources.debian.org)

Citations:


🏁 Script executed:

# Check if the file exists and examine its contents
fd "validTypeof.ts" --type f

Repository: biomejs/biome

Length of output: 223


🏁 Script executed:

# Read the test file to see the actual code
if [ -f "crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validTypeof.ts" ]; then
  cat -n "crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validTypeof.ts"
fi

Repository: biomejs/biome

Length of output: 1781


🏁 Script executed:

# Check nearby files to understand the test structure
ls -la "crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/" | head -20

Repository: biomejs/biome

Length of output: 1403


These examples will not compile in TypeScript.

Lines 26 and 32 reference variables declared in the function body within type annotations, which violates TypeScript's scoping rules. The return type typeof result and parameter type typeof defaults will fail with "Cannot find name" errors because type annotations are evaluated in the declaration scope, not the body scope. Move both constants outside their respective functions as suggested to make these valid test cases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/validTypeof.ts`
around lines 26 - 34, The tests use type annotations that reference variables
declared inside function bodies (return type typeof result in example3 and
parameter type typeof defaults in example4), which is invalid; move the
declarations for result and defaults out of their respective functions so the
types refer to variables in the declaration scope (e.g., declare const result =
... and const defaults = ... at module scope) and then update example3 and
example4 to use typeof result and typeof defaults respectively.

@ematipico ematipico merged commit 61f53ee into main Mar 17, 2026
18 checks passed
@ematipico ematipico deleted the fix/ambient-promotion branch March 17, 2026 14:35
@github-actions github-actions bot mentioned this pull request Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💅 [v2.4.7] Regression in noUnusedVariables rule when using variables for types

2 participants