Skip to content

Latest commit

Β 

History

History
113 lines (81 loc) Β· 3.18 KB

File metadata and controls

113 lines (81 loc) Β· 3.18 KB

no-useless-iterator-to-array

πŸ“ Disallow unnecessary .toArray() on iterators.

πŸ’Ό This rule is enabled in the following configs: βœ… recommended, β˜‘οΈ unopinionated.

πŸ”§πŸ’‘ This rule is automatically fixable by the --fix CLI option and manually fixable by editor suggestions.

Iterator.prototype.toArray() converts an iterator to an array. However, this conversion is unnecessary in the following cases:

  • The following builtins accept an iterable, so converting to an array first is unnecessary:

    • Map constructor
    • WeakMap constructor
    • Set constructor
    • WeakSet constructor
    • TypedArray constructor
    • Array.from(…)
    • TypedArray.from(…)
    • Object.fromEntries(…)
  • for…of can iterate over any iterable, so converting to an array first is unnecessary.

  • yield* can delegate to any iterable, so converting to an array first is unnecessary.

  • Promise.{all,allSettled,any,race}(…) accept an iterable, so .toArray() is unnecessary. However, removing it can change a synchronous throw into an asynchronous rejection when iteration fails, so these cases are reported as suggestions rather than autofixes.

  • The spread operator (...) works on any iterable, so converting to an array before spreading is unnecessary:

    • [...iterator.toArray()] β†’ [...iterator]
    • call(...iterator.toArray()) β†’ call(...iterator)
  • Some Array methods also exist on Iterator, so converting to an array to call them is unnecessary:

    • .every()
    • .find()
    • .forEach()
    • .reduce()
    • .some()

However, Array callbacks receive additional arguments (e.g., the 3rd array argument) that Iterator callbacks do not, so removing .toArray() can change behavior if the callback uses those arguments. These cases are reported as suggestions rather than autofixes.

This rule does not flag .filter(), .map(), or .flatMap() because their Iterator versions return iterators, not arrays, so the semantics differ.

Examples

// ❌
const set = new Set(iterator.toArray());

// βœ…
const set = new Set(iterator);
// ❌
const results = await Promise.all(iterator.toArray());

// βœ…
const results = await Promise.all(iterator);
// ❌
for (const item of iterator.toArray());

// βœ…
for (const item of iterator);
// ❌
function * foo() {
	yield * iterator.toArray();
}

// βœ…
function * foo() {
	yield * iterator;
}
// ❌
const items = [...iterator.toArray()];

// βœ…
const items = [...iterator];
// ❌
call(...iterator.toArray());

// βœ…
call(...iterator);
// ❌
iterator.toArray().every(fn);

// βœ…
iterator.every(fn);
// βœ… β€” `.filter()` returns an array on Array but an iterator on Iterator
iterator.toArray().filter(fn);