toSorted / toReversed / with — Immutable Array Methods
debt(d7/e3/b3/t5)
Closest to 'only careful code review or runtime testing' (d7). No detection tools are listed in detection_hints.tools. The bug caused by using sort()/reverse() instead of toSorted()/toReversed() on shared state is silent at parse and lint time — it manifests only when the mutated array produces unexpected UI or logic behaviour at runtime, requiring careful code review or testing to catch. Standard linters do not flag mutable sort on shared arrays by default.
Closest to 'simple parameterised fix' (e3). The quick_fix describes a direct method-name swap: arr.sort(fn) → arr.toSorted(fn), arr.reverse() → arr.toReversed(), and spread patterns → arr.with(i, v). This is slightly more than a trivial one-liner because you must audit all call sites where shared arrays are sorted/reversed, but each individual fix is a small, localised pattern replacement within one component.
Closest to 'localised tax' (b3). The applies_to covers web and cli broadly, but the actual burden is localised: only the places in the codebase that sort or reverse shared arrays are affected. It does not impose a cross-cutting architectural cost; once you adopt toSorted/toReversed at a given call site, the rest of the codebase is unaffected. The main ongoing tax is compatibility checking (ES2023/Node 20+) for teams supporting older environments.
Closest to 'notable trap' (t5). The misconception field highlights two documented gotchas: (1) developers assume toSorted() is meaningfully slower than sort(), which is false, causing unnecessary avoidance; and (2) with() looks like a setter but returns a new array rather than mutating in place — arr.with(2, 99) vs arr[2] = 99 is a documented source of confusion that most developers learn after encountering it. These are notable but well-documented surprises rather than catastrophic or deeply contradictory behaviours.
Also Known As
TL;DR
Explanation
JavaScript's sort(), reverse(), and direct index assignment mutate arrays in place — a common source of bugs when arrays are shared across components or passed to functions. ES2023 standardises immutable alternatives: Array.prototype.toSorted() returns a new sorted array without touching the original. Array.prototype.toReversed() returns a new reversed array. Array.prototype.with(index, value) returns a copy with the element at index replaced with value — equivalent to the spread pattern '[...arr.slice(0, i), value, ...arr.slice(i+1)]' but readable. Array.prototype.toSpliced() is the immutable splice(). These also work on TypedArrays.
Common Misconception
Why It Matters
Common Mistakes
- Using toSorted() in environments that don't support ES2023 — check browser compatibility or add a polyfill; Node.js 20+ supports all four methods.
- Confusing with() with a setter — arr.with(2, 99) returns a new array; arr[2] = 99 mutates in place. They are not interchangeable.
- Not using toSorted() in React or similar frameworks where prop mutation causes silent bugs — this is the primary use case for these methods.
- Forgetting toSpliced() — toSorted, toReversed, and with() are well-known, but toSpliced() (immutable splice) is less remembered and equally useful.
Code Examples
// ❌ sort() mutates the original — React state bug
function ProductList({ products }) {
const sorted = products.sort((a, b) => a.price - b.price);
// products (the prop) is now mutated!
// Parent component's state changed without a setState call
return sorted.map(p => <Product key={p.id} {...p} />);
}
// ❌ Verbose immutable update
const updated = [
...items.slice(0, index),
newItem,
...items.slice(index + 1),
];
// ✅ toSorted() — original unchanged
function ProductList({ products }) {
const sorted = products.toSorted((a, b) => a.price - b.price);
// products is untouched
return sorted.map(p => <Product key={p.id} {...p} />);
}
// ✅ with() — readable single-item update
const updated = items.with(index, newItem);
// ✅ Full set of ES2023 immutable methods
const arr = [3, 1, 4, 1, 5];
const sorted = arr.toSorted(); // [1, 1, 3, 4, 5]
const reversed = arr.toReversed(); // [5, 1, 4, 1, 3]
const replaced = arr.with(2, 99); // [3, 1, 99, 1, 5]
const spliced = arr.toSpliced(1, 2, 9); // [3, 9, 1, 5]
// arr is still [3, 1, 4, 1, 5] throughout