oxc-project/oxc · warning · OxcDiagnostic

Avoid using a negative index with `Array#with()`.

Error message

Avoid using a negative index with `Array#with()`.

What it means

Diagnostic from the oxlint rule `unicorn/no-confusing-array-with` (negative-index arm, category: suspicious). `Array.prototype.with(index, value)` interprets a negative index as an offset from the end of the array — unlike `slice()`/`splice()` intuition — so `array.with(-1, v)` replaces the last element. Because that surprises most readers, the rule flags statically negative indexes and asks for a non-negative position instead.

Source

Thrown at crates/oxc_linter/src/rules/unicorn/no_confusing_array_with.rs:14

use oxc_ast::{
    AstKind,
    ast::{Expression, UnaryOperator},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;

use crate::{
    AstNode, ast_util::is_method_call, context::LintContext, rule::Rule, utils::is_same_expression,
};

fn negative_index_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Avoid using a negative index with `Array#with()`.")
        .with_note("`Array#with()` interprets a negative index as an offset from the end.")
        .with_help("Use a non-negative index to make the intended position explicit.")
        .with_label(span)
}

fn length_index_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Avoid using `.length` as the index in `Array#with()`.")
        .with_note("An array's `.length` is one past its last valid index.")
        .with_help("Use `.length - 1` to replace the last element.")
        .with_label(span)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ConfusingWithIndex {
    Negative,
    Length,
}

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Rewrite with an explicit non-negative index: `array.with(array.length - 1, value)` targets the last element
  2. Store the position in a variable (`const last = items.length - 1;`) when the same offset is reused
  3. If the end-relative offset is intentional and agreed, suppress once with `// oxlint-disable-next-line unicorn/no-confusing-array-with`

Example fix

// before
const updated = items.with(-1, 'last');

// after
const updated = items.with(items.length - 1, 'last');
Defensive patterns

Strategy: validation

Validate before calling

// explicit, lint-clean helper for end-relative replacement
function withFromEnd(arr, offsetFromEnd, value) {
  const index = arr.length - 1 - offsetFromEnd;
  if (index < 0) throw new RangeError('offset past start');
  return arr.with(index, value);
}

Prevention

When it happens

Trigger: Any `x.with(idx, ...)` call whose first argument statically evaluates to a negative number: `array.with(-1, value)`, `array.with(-2, value)`, `array.with(-1.5, value)`, `array.with(-(-(-1)))`. Dynamic indexes (`array.with(i)`), `-0`, `+1`, and `-1e400` (not finite-negative by truncation rules) pass.

Common situations: Porting `.at(-1)` or negative-splice idioms to the newer ES2023 `.with()`; adopting change-by-copy methods in codebases that had polyfills with different semantics; code review disputes about negative indexes.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/2bd334d39103d8a4. Report an issue: GitHub.