oxc/no-map-spread
Disallow object and array spreads in the return value of map and flatMap callbacks
tasks.map(task => ({ ...task, done: false })) copies every property of every item into a new object. On a large array, that's a lot of work and memory. If nothing else uses the old items, you can change them in place.
The rule looks at the object or array the callback returns, directly or through a variable. It doesn't look at what's being spread, so ids.map(id => ({ ...defaults, id })) is reported too.
--fix-suggestions rewrites { ...task, done: false } to Object.assign(task, { done: false }). That changes the original array's items.
Configure#
Off by default. To turn it on:
[lint.rules]
"oxc/no-map-spread" = "error" # or "warn"Every option, set to its default:
[lint.rules]
"oxc/no-map-spread" = ["error", {
ignoreRereads = true,
ignoreArgs = true,
}]Examples#
function loadTasks() {
const tasks = readTasks();
return tasks.mapSpreading to modify object properties in map calls is inefficient(task => ({ ...task, done: false }));
}const rows = readRows().mapSpreading to modify array elements in map calls is inefficient(row => [...row, total(row)]);function loadTasks() {
const tasks = readTasks();
return tasks.map(task => Object.assign(task, { done: false }));
}const rows = readRows();
for (const row of rows) {
row.push(total(row));
}An array that belongs to this is never reported, because changing its items would change the instance:
class TaskList {
snapshot() {
return this.tasks.map(task => ({ ...task }));
}
}Options#
| Option | Default | What it does |
|---|---|---|
ignoreRereads | true | Allow the spread if the array variable is read again after the call. In that case, the copy is probably on purpose. |
ignoreArgs | true | Allow the spread if the array is a function parameter. The caller might still use its items. |
Allowed by default, because tasks is used afterwards:
const tasks = readTasks();
const drafts = tasks.map(task => ({ ...task, done: false }));
console.log(tasks.length, drafts.length);{ "ignoreRereads": false }1 problemconst tasks = readTasks();
const drafts = tasks.mapSpreading to modify object properties in map calls is inefficient(task => ({ ...task, done: false }));
console.log(tasks.length, drafts.length);Allowed by default, because tasks is a parameter:
function markDone(tasks) {
return tasks.map(task => ({ ...task, done: true }));
}{ "ignoreArgs": false }1 problemfunction markDone(tasks) {
return tasks.mapSpreading to modify object properties in map calls is inefficient(task => ({ ...task, done: true }));
}When not to use it#
If your code treats data as immutable, as React state and Redux stores require, the copy is the point. The cost also doesn't matter for small arrays.