This rule raises an issue when the forEach method is called on arrays, as for…of loops provide more control flow
options.
The usage of forEach limits your control flow options. You cannot use break to exit early or continue to
skip iterations, as these statements don’t work inside callback functions. This forces developers to use workarounds like throwing exceptions or
complex conditional logic.
Additionally, forEach doesn’t work properly with async/await. The callback function in forEach is called for each element
immediately without waiting for any asynchronous operations to complete, which can lead to unexpected behavior when processing arrays that require
async operations.
In TypeScript projects, forEach creates a function boundary that breaks type narrowing. Variables that were narrowed to specific types
before the loop may lose their narrowed type inside the callback. Additionally, TypeScript’s analysis of variable mutations can be disrupted by the
function boundary, potentially missing important usage patterns.
The for…of loop syntax is also more readable and familiar to developers coming from other programming languages, making the code
easier to understand and maintain.
Using forEach instead of for…of can lead to:
Replace simple forEach calls with for…of loops.
array.forEach(element => {
console.log(element);
}); // Noncompliant
array.forEach((element, index) => {
console.log(element, index);
}); // Noncompliant
// Doesn't work as expected with async operations
urls.forEach(async (url) => {
const response = await fetch(url); // These requests happen concurrently, not sequentially
console.log(response.status);
}); // Noncompliant
for (const element of array) {
console.log(element);
}
for (const [index, element] of array.entries()) {
console.log(element, index);
}
// Properly handles async operations sequentially
for (const url of urls) {
const response = await fetch(url); // These requests happen one after another
console.log(response.status);
}