Skip to content

linter: unicorn/prefer-array-some autofix on find()/findLast() === undefined naively swaps tokens, producing always-true/always-false comparisons #22089

@zwang180

Description

@zwang180

What version of Oxlint are you using?

1.62.0

What command did you run?

oxlint --fix test.ts

What does your .oxlintrc.json (or oxlint.config.ts) config file look like?

{
  "categories": {
    "correctness": "off"
  },
  "plugins": ["unicorn"],
  "rules": {
    "unicorn/prefer-array-some": "error"
  }
}

What happened?

Disclosure: this issue was drafted with AI assistance and reviewed by me before submission, per the AI Usage Policy

What happened?

The auto-fix performs a literal find/findLastsome token swap and leaves the surrounding === undefined/!== undefined/== null/!= null comparison untouched. Since Array#some() always returns a boolean, the comparison becomes a constant — silently inverting (or trivializing) the branch logic.

Minimal reproduction

// test.ts
const arr = [1, 2, 3];
if (arr.find((x) => x === 2) === undefined) {
  console.log('not found');
} else {
  console.log('found');
}

After oxlint --fix

const arr = [1, 2, 3];
if (arr.some((x) => x === 2) === undefined) {  // ← always false
  console.log('not found');                    // ← unreachable
} else {
  console.log('found');                        // ← always taken
}

All affected variants

All five comparison variants exhibit the same naive token-swap behavior:

Input Auto-fixed to Result
arr.find(p) === undefined arr.some(p) === undefined always false
arr.find(p) !== undefined arr.some(p) !== undefined always true
arr.find(p) == null arr.some(p) == null always false
arr.find(p) != null arr.some(p) != null always true
arr.findLast(p) === undefined arr.some(p) === undefined always false
arr.findLast(p) !== undefined arr.some(p) !== undefined always true

Runtime verification

const empty = [];
const has2 = [1, 2, 3];
// Original — correct
empty.find(x => x === 2) === undefined  // true  (no match)
has2.find(x => x === 2) === undefined   // false (match)
// After autofix — broken
empty.some(x => x === 2) === undefined  // false (always)
has2.some(x => x === 2) === undefined   // false (always)

This caused a real production bug in our codebase where a deduplication check became inverted, causing every value to be flagged as a duplicate.

Comparison with upstream eslint-plugin-unicorn

Per the upstream rule docs:

This rule is fixable for .filter(…).length checks and .{findIndex,findLastIndex}(…)
This rule provides a suggestion for .{find,findLast}(…)

The upstream plugin deliberately classifies .find()/.findLast() rewrites as suggestions only (require human review) — quoting the original PR:

"because autofixing isn't safe in all cases." It seems Oxlint's port treats this as an autofix instead.

Expected behavior

Either:

  1. Produce a semantically correct fix that handles the surrounding comparison:
    arr.find(p) === undefined/== null → !arr.some(p)
    arr.find(p) !== undefined/!= null → arr.some(p)
  2. Mark the rewrite as a suggestion (not autofix) for .find()/.findLast() patterns, matching upstream behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions