Skip to content

refactor(linters): add prefer-coerce-object rule - #45489

Draft
secustor wants to merge 3 commits into
renovatebot:mainfrom
secustor:refactor/45216-prefer-coerce-object
Draft

refactor(linters): add prefer-coerce-object rule#45489
secustor wants to merge 3 commits into
renovatebot:mainfrom
secustor:refactor/45216-prefer-coerce-object

Conversation

@secustor

@secustor secustor commented Aug 26, 2026

Copy link
Copy Markdown
Member

Changes

Important

Stacked on #45488 (prefer-coerce-array). Only refactor(linters): add prefer-coerce-object rule is new here; the other two commits belong to #45488. The two rewrites touch overlapping lines in several files, so they are best reviewed and merged in order. GitHub cannot base a PR on a branch in a fork, so this one targets main and carries #45488's commits until that one lands.

The object counterpart to prefer-coerce-array: adds a renovate/prefer-coerce-object oxlint rule and applies it across lib/.

x ?? {} becomes coerceObject(x). The rule flags a ?? whose default is a bare {} literal, so a populated default (x ?? { a: 1 }) and an asserted one (x ?? ({} as Foo)) still pass — the latter keeps lib/util/object.ts's own definition from tripping over itself. A x ?? y ?? {} chain maps onto the two-argument coerceObject(x, y).

The rule is enabled for lib/** (including specs), alongside prefer-coerce-array. Every one of the 65 hits converted cleanly, so unlike the array rule this one needed no disable comments.

Context

Please select one of the following:

AI assistance disclosure

Did you use AI tools to create any part of this pull request?

  • No — I did not use AI for this contribution.
  • Yes — minimal assistance (e.g., IDE autocomplete, small code completions, grammar fixes).
  • Yes — substantive assistance (AI-generated non‑trivial portions of code, tests, or documentation).
  • Yes — other (please describe):

Claude Opus 5 via Claude Code wrote the lint rule and its tests, and ran the codemod that applied the rule across lib/.

Use of AI in replying to PR comments

Who answers review comments:

  • @secustor will read and reply directly. Name the account.
  • An agent will draft replies and @username will read them before they are posted. Name the account.
  • Nobody has explicitly committed to replying.

Documentation (please check one with an [x])

  • I have updated the documentation, or
  • No documentation update is required

How I've tested my work (please select one)

I have verified these changes via:

  • Code inspection only, or
  • Newly added/modified unit tests, or
  • No unit tests, but ran on a real repository, or
  • Both unit tests + ran on a real repository

tools/lint/rules/prefer-coerce-object.spec.ts covers the rule itself. The existing suite covers the rewritten call sites; tsc --noEmit, oxlint, biome and prettier are clean, and vitest run --changed passes apart from two failures that also reproduce on main in this environment (lib/logger/pretty-stdout.spec.ts colour detection and a git clone submodule test blocked by protocol.file.allow).

Closes renovatebot#45217

`x ?? []` and `coerceArray(x)` say the same thing, but only the second
one is searchable and consistent. The rule flags a `??` whose default is
a bare `[]` literal; a non-empty default or an asserted `([] as Foo[])`
still passes.

Applying it across `lib/` surfaced three spots the helper cannot cover,
which keep `?? []` with a disable comment or a narrower fix:

- `package-files.ts` destructures a tuple, which `coerceArray()` widens
- `generate.spec.ts` defaults `matchAll()`, an iterator rather than an array
- `mise/upgradeable-tooling.ts` declares a `Record` return, so its
  `?? []` fallback was wrong to begin with and becomes `coerceObject()`
Closes renovatebot#45216

The object counterpart to `prefer-coerce-array`: `x ?? {}` becomes
`coerceObject(x)`. The rule flags a `??` whose default is a bare `{}`
literal, so a populated default or an asserted `({} as Foo)` still
passes; a `x ?? y ?? {}` chain maps onto `coerceObject(x, y)`.

Every hit across `lib/` converted cleanly, so no disable comments were
needed.
`Object.entries(...).find()` returns a tuple, and defaulting it with
`?? []` only to destructure the second element reads as an array
default when it is not one. Name the found entry instead, which drops
the `?? []` rather than routing a tuple through `coerceArray()`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: find usages that should be coerceObject

1 participant