Skip to content

no-duplicate-constant-values: null-valued constants use the generic threshold-of-2, undercutting the rule's own small-value-spac [Content truncated due to length] #51526

Description

@github-actions

Rule

no-duplicate-constant-values (eslint-factory/src/rules/no-duplicate-constant-values.ts) — first review since it shipped; no prior issues filed.

What the rule does

Groups module-level const NAME = <static primitive> declarations by value and reports later duplicates once a value's group reaches a minimum size. The thresholds are deliberately asymmetric, and the code comment explains why:

const MIN_NUMERIC_DUPLICATE_GROUP_SIZE = 3;
// Booleans only have 2 possible values module-wide, an even smaller space than "small numbers",
// so unrelated constants coincidentally sharing `true`/`false` are at least as likely as the
// numeric case above. Apply the same minimum-group-size guard to avoid false positives.
const MIN_BOOLEAN_DUPLICATE_GROUP_SIZE = 3;

Everything else (strings, template literals, regexps — and, by omission, null) falls through to the generic minGroupSize = 2 in Program:exit:

const minGroupSize = valueKey.startsWith("number:") ? MIN_NUMERIC_DUPLICATE_GROUP_SIZE : valueKey.startsWith("boolean:") ? MIN_BOOLEAN_DUPLICATE_GROUP_SIZE : 2;

The gap

getStaticValueKey maps a null literal to `${typeof node.value}:${String(node.value)}` i.e. "object:null", which isn't number:/boolean:-prefixed, so it gets the generic threshold of 2. But null has exactly one possible value — a strictly smaller value space than booleans (2 values, which the rule already guards at 3) or "small numbers" (the numeric guard's stated justification). By the rule's own reasoning for the boolean guard, null is more prone to coincidental, semantically-unrelated matches than booleans are, yet it's the one primitive type left without a raised threshold. Two unrelated placeholder/not-yet-initialized constants such as:

const CACHED_CLIENT = null;
const PENDING_HANDLE = null;

would be flagged as "duplicating" each other at group size 2, which is exactly the kind of coincidental-match false positive the boolean guard was added to prevent. No live occurrence was found in actions/setup/js/**/*.cjs today (grep found no module-level const X = null; declarations), so this is a soundness/consistency gap rather than a currently-firing false positive — same category as the previously-filed, ungrounded-but-accepted require-fs-sync-try-catch catch-less-try/finally finding.

Suggested fix

Either add a MIN_NULL_DUPLICATE_GROUP_SIZE (align it with the boolean guard, i.e. 3, or higher since the value space is smaller still), or exclude null/undefined-valued constants from this rule entirely on the grounds that reusing null as a sentinel initializer across unrelated constants is idiomatic and carries no useful signal.

Acceptance criteria

  • Add a valid-case test: two unrelated const declarations both initialized to null at group size 2 are NOT reported.
  • If keeping null in scope with a raised threshold, add an invalid-case test at the new minimum group size (mirroring the existing boolean test structure).
  • Existing string/number/boolean/regexp behavior and tests remain unchanged.

Generated by 🤖 ESLint Refiner · agent · 224.5 AIC · ⌖ 40.7 AIC · ⊞ 4.9K ·

  • expires on Aug 15, 2026, 9:40 PM UTC-08:00

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions