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);
}