Skip to content

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

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

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions