Skip to content

noUnreadableForOfExpression (JavaScript)

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

Disallow complex expressions as the value that a for...of loop goes through.

The parentheses of a for...of or for await...of loop hold two things: the variable that receives each item, and the value that the loop goes through. When that value is a long or nested expression, it is harder to see what the loop works on. Storing the value in a variable before the loop gives it a name and keeps the loop easy to scan.

The rule accepts these expressions as the value to loop over:

  • A variable, this, an array such as [first, second], or a literal such as a string in quotes. A template string (a string in backticks) is not accepted here.
  • A property read with a dot, such as object.items, when the part before the dot is also in this list.
  • A call such as getItems(argument) or object.getItems(argument). The function must be a variable or a property that is in this list, and every argument must be short. A short argument is a variable, a literal, a template string, a property read with a dot, a call without arguments, or an array of those.

The rule reports every other expression. Examples are a call that takes another call with arguments, a new expression, an await expression, a condition such as first || second, optional chaining (items?.values()), and a property read with brackets (items[index]). A method call on such an expression is also reported, as in (first || second).values().

Two kinds of method calls follow their own rules:

  • A call to .map() or .filter() that produces the value to loop over is not reported, whatever its arguments are.
  • A call to .map(), .filter(), .flatMap(), .take(), or .drop() on an iterator is always reported. An iterator is an object that produces its values one at a time. The rule recognizes the iterators returned by .entries(), .keys(), .values(), .matchAll(), methods called on Iterator itself such as Iterator.from(), and generator functions (function*) declared in the same file. It also follows a const variable declared before the loop to the value that the variable holds.
for (const item of getItems(createArgument(seed))) {
console.log(item);
}
code-block.js:1:20 lint/nursery/noUnreadableForOfExpression ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ℹ This for...of loop goes through the result of a complex expression.

> 1 │ for (const item of getItems(createArgument(seed))) {
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ console.log(item);
3 │ }

ℹ A complex expression between the parentheses of a loop makes it harder to see what the loop works on.

ℹ Store the expression in a variable before the loop, and loop over that variable instead.

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

for (const key of Object.keys(Object.fromEntries(entries))) {
console.log(key);
}
code-block.js:1:19 lint/nursery/noUnreadableForOfExpression ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ℹ This for...of loop goes through the result of a complex expression.

> 1 │ for (const key of Object.keys(Object.fromEntries(entries))) {
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ console.log(key);
3 │ }

ℹ A complex expression between the parentheses of a loop makes it harder to see what the loop works on.

ℹ Store the expression in a variable before the loop, and loop over that variable instead.

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

for (const item of new Set(items)) {
console.log(item);
}
code-block.js:1:20 lint/nursery/noUnreadableForOfExpression ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ℹ This for...of loop goes through the result of a complex expression.

> 1 │ for (const item of new Set(items)) {
│ ^^^^^^^^^^^^^^
2 │ console.log(item);
3 │ }

ℹ A complex expression between the parentheses of a loop makes it harder to see what the loop works on.

ℹ Store the expression in a variable before the loop, and loop over that variable instead.

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

for (const value of items.values().map(callback)) {
console.log(value);
}
code-block.js:1:21 lint/nursery/noUnreadableForOfExpression ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ℹ This for...of loop goes through the result of a complex expression.

> 1 │ for (const value of items.values().map(callback)) {
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │ console.log(value);
3 │ }

ℹ A complex expression between the parentheses of a loop makes it harder to see what the loop works on.

ℹ Store the expression in a variable before the loop, and loop over that variable instead.

ℹ 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 argument = createArgument(seed);
for (const item of getItems(argument)) {
console.log(item);
}
for (const item of object.items) {
console.log(item);
}
for (const value of Object.values(object)) {
console.log(value);
}
for (const [index, item] of [first, second].entries()) {
console.log(index, item);
}
for (const item of items.filter(item => item.isEnabled)) {
console.log(item);
}