# 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.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

Off by default. To turn it on:

```toml bunfig.toml
[lint.rules]
"oxc/no-map-spread" = "error" # or "warn"
```

Every option, set to its default:

```toml bunfig.toml
[lint.rules]
"oxc/no-map-spread" = ["error", {
  ignoreRereads = true,
  ignoreArgs = true,
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad */}
```js
function loadTasks() {
  const tasks = readTasks();
  return tasks.map(task => ({ ...task, done: false }));
}
```

{/* bad */}
```js
const rows = readRows().map(row => [...row, total(row)]);
```

Not reported:

{/* good */}
```js
function loadTasks() {
  const tasks = readTasks();
  return tasks.map(task => Object.assign(task, { done: false }));
}
```

{/* good */}
```js
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:

{/* good */}
```js
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:

{/* good */}
```js
const tasks = readTasks();
const drafts = tasks.map(task => ({ ...task, done: false }));
console.log(tasks.length, drafts.length);
```

{/* bad {"options": [{"ignoreRereads": false}]} */}
```js
const tasks = readTasks();
const drafts = tasks.map(task => ({ ...task, done: false }));
console.log(tasks.length, drafts.length);
```

Allowed by default, because `tasks` is a parameter:

{/* good */}
```js
function markDone(tasks) {
  return tasks.map(task => ({ ...task, done: true }));
}
```

{/* bad {"options": [{"ignoreArgs": false}]} */}
```js
function markDone(tasks) {
  return tasks.map(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.
