Skip to content

linter(no-underscore-dangle): add allowAfterUsing option for explicit resource management bindings #22322

@tinovyatkin

Description

@tinovyatkin

Summary

Add an allowAfterUsing option (default false) to no-underscore-dangle so that leading-underscore names are permitted specifically on using / await using declarators. The existing per-context allow-toggles (allowFunctionParams, allowInArrayDestructuring, allowInObjectDestructuring, etc.) already carve out each place where a leading-_ identifier is idiomatic; using is the modern addition that the rule doesn't yet know about.

Motivation

TC39's Explicit Resource Management proposal (now Stage 4 / ES2026, shipped in Node.js 24 and the current TypeScript releases) introduces using and await using. The common pattern is:

using _guard = enterCriticalSection();
// the binding's only job is to be disposed at scope exit —
// it's intentionally never read
doWork();

Because the binding is never read by name, users typically prefix it with _ to:

  1. Signal intent to readers ("this is for its side-effect on scope exit").
  2. Silence no-unused-vars (ESLint and oxlint both accept a leading _ via argsIgnorePattern / varsIgnorePattern defaults or conventions).

Today this collides with no-underscore-dangle. Users have three workarounds, all unsatisfying:

  • Drop the _, which then trips no-unused-vars (TypeScript's noUnusedLocals also fires: TS6133).
  • Add each name to allow: [...] — but the list grows (_guard, _cycleGuard, _lock, _tx, _span, …) and requires global coordination for a local pattern.
  • Disable the rule inline on every using line.

None of these match the spirit of the rule's existing options, which already grant targeted escape hatches for language/runtime contexts where the underscore convention is useful (this._x via allowAfterThis, destructuring placeholders, function parameter ignores, etc.).

Proposed option

{
  "rules": {
    "no-underscore-dangle": ["error", {
      "allowAfterUsing": true  // default: false
    }]
  }
}

Semantics: when the binding identifier is declared via using or await using, a leading _ does not trigger the rule. Trailing _ is still rejected (matches the rest of the rule's behavior).

Implementation sketch (the rule lives at crates/oxc_linter/src/rules/eslint/no_underscore_dangle.rs): check_binding currently reaches VariableDeclarator and returns BindingContext::Plain. A branch on VariableDeclaration.kind == VariableDeclarationKind::Using | AwaitUsing can return a new BindingContext::Using (or a boolean flag), which the top-level check then consults alongside allowAfterUsing.

Prior art

Happy to open a PR if the maintainers like the direction — wanted to check interest first, since this is an extension to an ESLint rule and you may prefer to upstream it there.

Example

// with { allowAfterUsing: true }
using _cycleGuard = enterPath(onPath, value);  // ✅ no warning
using __wat = foo();                            // ❌ still flagged (trailing not at issue here, but leading __)
using trailing_ = foo();                        // ❌ still flagged (trailing _)

// in a regular declaration — unaffected by the new option
const _temp = x;  // ❌ still flagged (unless allow / other existing options apply)

Environment

  • oxlint version: 1.63.0
  • Rule: eslint/no-underscore-dangle

Thank you for the excellent linter — and for the thorough option set on this rule; one more would be lovely. 🙏

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions