Skip to content

noMapSpread (JavaScript)

Language JavaScript (and super languages)
biome.json
{
"linter": {
"rules": {
"nursery": {
"noMapSpread": "error"
}
}
}
}

Disallow spreading objects or arrays in the values returned by map() and flatMap() callbacks.

A spread (...) inside an object or array literal creates a new object or array and copies every property or element into it. When the callback of map() or flatMap() does this, the copy happens once for each item in the array, which is slow and uses extra memory for large arrays.

When it’s fine to change the original items, use Object.assign() or assign the new properties directly. For arrays, add the new elements with push().

Copying is still useful when other code needs the original items to stay unchanged. To avoid reporting those cases, the rule ignores map() and flatMap() calls on:

  • arrays read from this, such as this.items.map(...);
  • variables that are read again after the call (see the ignoreRereads option);
  • function parameters (see the ignoreArgs option).

The rule follows variables that the callback declares and returns, so it also reports const copy = { ...item }; return copy;.

The fix replaces an object that starts with a spread and has at least one other property with a call to Object.assign(). This changes the first spread object in place, so the fix is unsafe. The fix isn’t offered for an object that only contains a spread, such as { ...item }, or for an object that contains comments.

const users = getUsers();
const rows = users.map((user) => ({ ...user, role: getRole(user) }));
code-block.js:2:35 lint/nursery/noMapSpread  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This object spread copies an object for each item in the map() callback.

1 │ const users = getUsers();
> 2 │ const rows = users.map((user) => ({ ...user, role: getRole(user) }));
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3 │

ℹ The callback runs once for each item in the array.

1 │ const users = getUsers();
> 2 │ const rows = users.map((user) => ({ ...user, role: getRole(user) }));
│ ^^^
3 │

ℹ A spread creates a new object and copies every property into it. Doing this for each item is slow and uses extra memory for large arrays.

ℹ If it's fine to change the original items, add the properties with Object.assign() or assign them directly. Keep the spread if other code needs the original items to stay unchanged.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

ℹ Unsafe fix: Use Object.assign() instead.

1 1 │ const users = getUsers();
2 │ - const·rows·=·users.map((user)·=>·({·...user,·role:·getRole(user)·}));
2 │ + const·rows·=·users.map((user)·=>·Object.assign(user,·{·role:·getRole(user)·}));
3 3 │

const groups = getGroups();
const lists = groups.flatMap((group) => [...group.members, group.owner]);
code-block.js:2:41 lint/nursery/noMapSpread ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This array spread copies an array for each item in the flatMap() callback.

1 │ const groups = getGroups();
> 2 │ const lists = groups.flatMap((group) => [...group.members, group.owner]);
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3 │

ℹ The callback runs once for each item in the array.

1 │ const groups = getGroups();
> 2 │ const lists = groups.flatMap((group) => [...group.members, group.owner]);
│ ^^^^^^^
3 │

ℹ A spread creates a new array and copies every element into it. Doing this for each item is slow and uses extra memory for large arrays.

ℹ If it's fine to change the original arrays, add the elements with push(). Keep the spread if other code needs the original arrays to stay unchanged.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

const users = getUsers();
const rows = users.map((user) => Object.assign(user, { role: getRole(user) }));
class UsersStore {
#users = [];
get users() {
// Each caller gets its own copies, so changing them can't change the store.
return this.#users.map((user) => ({ ...user }));
}
}

Default: true

Ignores map() and flatMap() calls on a variable that is read again after the call. Code that reads the original array later may rely on the copies to keep the original items unchanged.

const users = getUsers();
const rows = users.map((user) => ({ ...user, role: getRole(user) }));
console.log(users);

Set it to false to report these calls too.

biome.json
{
"linter": {
"rules": {
"nursery": {
"noMapSpread": {
"level": "on",
"options": {
"ignoreRereads": false
}
}
}
}
}
}
const users = getUsers();
const rows = users.map((user) => ({ ...user, role: getRole(user) }));
console.log(users);
code-block.js:2:35 lint/nursery/noMapSpread  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This object spread copies an object for each item in the map() callback.

1 │ const users = getUsers();
> 2 │ const rows = users.map((user) => ({ ...user, role: getRole(user) }));
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3 │ console.log(users);
4 │

ℹ The callback runs once for each item in the array.

1 │ const users = getUsers();
> 2 │ const rows = users.map((user) => ({ ...user, role: getRole(user) }));
│ ^^^
3 │ console.log(users);
4 │

ℹ A spread creates a new object and copies every property into it. Doing this for each item is slow and uses extra memory for large arrays.

ℹ If it's fine to change the original items, add the properties with Object.assign() or assign them directly. Keep the spread if other code needs the original items to stay unchanged.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

ℹ Unsafe fix: Use Object.assign() instead.

1 1 │ const users = getUsers();
2 │ - const·rows·=·users.map((user)·=>·({·...user,·role:·getRole(user)·}));
2 │ + const·rows·=·users.map((user)·=>·Object.assign(user,·{·role:·getRole(user)·}));
3 3 │ console.log(users);
4 4 │

Default: true

Ignores map() and flatMap() calls on a function parameter. The caller owns the array, so changing its items in place could affect code outside the function.

function withRoles(users) {
return users.map((user) => ({ ...user, role: getRole(user) }));
}

Set it to false to report these calls too.

biome.json
{
"linter": {
"rules": {
"nursery": {
"noMapSpread": {
"level": "on",
"options": {
"ignoreArgs": false
}
}
}
}
}
}
function withRoles(users) {
return users.map((user) => ({ ...user, role: getRole(user) }));
}
code-block.js:2:31 lint/nursery/noMapSpread  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This object spread copies an object for each item in the map() callback.

1 │ function withRoles(users) {
> 2 │ return users.map((user) => ({ ...user, role: getRole(user) }));
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3 │ }
4 │

ℹ The callback runs once for each item in the array.

1 │ function withRoles(users) {
> 2 │ return users.map((user) => ({ ...user, role: getRole(user) }));
│ ^^^
3 │ }
4 │

ℹ A spread creates a new object and copies every property into it. Doing this for each item is slow and uses extra memory for large arrays.

ℹ If it's fine to change the original items, add the properties with Object.assign() or assign them directly. Keep the spread if other code needs the original items to stay unchanged.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

ℹ Unsafe fix: Use Object.assign() instead.

1 1 │ function withRoles(users) {
2 │ - ··return·users.map((user)·=>·({·...user,·role:·getRole(user)·}));
2 │ + ··return·users.map((user)·=>·Object.assign(user,·{·role:·getRole(user)·}));
3 3 │ }
4 4 │