Lint rule: prefer ...(cond && obj) over ...(cond ? obj : {})
Status#
Done, pending review. Rule + tests in commit 2; repo-wide --fix in commit 3.
PR: https://github.com/iterate/iterate/pull/2487
Summary#
Add a deterministic, auto-fixable custom oxlint rule (iterate/prefer-logical-and-spread)
that rewrites
const x = {
...(f ? { abc: f.def } : {}),
};to
const x = {
...(f && { abc: f.def }),
};Object spread of any falsy value is a no-op (same as spreading {}), so the
ternary's : {} arm is dead weight.
Decisions (assumptions, made while Misha is AFK)#
- Scope: only spreads inside object literals (
SpreadElementwhose parent is anObjectExpression). Array spread of falsy throws, so it's excluded. JSX spread attributes left out to keep the rule tight. - Only the exact stated direction: alternate must be an empty object literal
{}. The mirroredcond ? {} : objform is NOT rewritten to!cond && obj(adds a negation, less obviously a pure win). - The consequent can be any expression (e.g.
f ? f.extras : {}→f && f.extras) — same falsy-spread reasoning applies. - Fix wraps the test/consequent in parens when precedence demands it
(
a || b ? x : {}→(a || b) && x;f ? (a ?? b) : {}→f && (a ?? b)), so the fix is always parse- and semantics-preserving. - If comments live inside the ternary but outside the test/consequent nodes, report without a fix rather than silently dropping them.
Checklist#
- add
iterate/prefer-logical-and-spreadrule tolint/oxlint-plugin-iterate.tsimplemented with aneedsParensInsideLogicalAndprecedence helper; reports fix-less when the rewrite would drop a comment - enable it as
errorin.oxlintrc.jsonalongside the otheriterate/*rules - test file
lint/oxlint-plugin-logical-and-spread.test.tscovering report, fix output (incl. precedence parens), and non-matches 5 tests, runs the real oxlint binary with--fixand asserts the rewritten files - run
pnpm lint:fixacross the repo, commit the mechanical fixes separately 71 files; see notes
Implementation notes#
oxlint --fixneeded two passes: fixes for conditional spreads nested inside another conditional spread's consequent overlap the outer fix, so oxlint defers them to the next run (7 such sites, e.g.apps/os/src/lib/agent-round-meta-yaml.ts).- Three sites in
scripts/preview/{e2e-telemetry,preview}.tsspread on anunknown-typed test (caught errors), and...(unknown && {…})is TS2698. Hand-tweaked to...(!!input.error && {…})— identical runtime semantics, and!!makes the spread typefalse | {…}.