Skip to content

@studnicky/prefer-collection-types ​

Flags four patterns where arrays or plain objects perform worse than Set or Map:

  • Pattern A: Inline array literal membership using .includes() or an indexOf() comparison — use new Set([...]).has(value).
  • Pattern B: An Object.fromEntries() result accessed through computed brackets, directly or through a const binding — use new Map(pairs).get(key).
  • Pattern C: A const array used exclusively for .includes() or indexOf() membership tests, in any scope — declare it as new Set(...).
  • Pattern D: Array literal .includes() inside .filter(), .some(), .every(), .find(), or .findIndex() callbacks — convert the array to a Set.

Set.has is 29× faster than Array.includes on equal-size inputs.

The rule recognizes membership-style indexOf() comparisons in any of their equivalent forms: x.indexOf(y) !== -1, x.indexOf(y) === -1 (negated), x.indexOf(y) > -1, x.indexOf(y) < 0, and x.indexOf(y) >= 0 (negated). Each of these is treated the same as an .includes() call for Patterns A and C.

Fixable: No · Options: checkArrayLiterals, checkFromEntries, checkModuleScopeArrays · Suggested severity: error

✗ Incorrect ​

ts
// Pattern A — inline array .includes()
if (["admin", "moderator", "owner"].includes(role)) {
  /* ... */
}
ts
// Pattern C — const array used only for membership checks
const VALID_METHODS = ["GET", "POST", "PUT", "DELETE"];
if (VALID_METHODS.includes(method)) {
  /* ... */
}
ts
// Pattern D — .includes() inside iteration callback
const active = users.filter((u) => ["active", "pending"].includes(u.status));
ts
// Pattern B — Object.fromEntries accessed via computed key
const value = Object.fromEntries(pairs)[key];

✓ Correct ​

ts
// Use Set for membership tests
if (new Set(["admin", "moderator", "owner"]).has(role)) {
  /* ... */
}
ts
// Module-scope Set
const VALID_METHODS = new Set(["GET", "POST", "PUT", "DELETE"]);
if (VALID_METHODS.has(method)) {
  /* ... */
}
ts
// Set inside iteration callback
const ACTIVE_STATUSES = new Set(["active", "pending"]);
const active = users.filter((u) => ACTIVE_STATUSES.has(u.status));
ts
// Use Map instead of fromEntries + bracket access
const map = new Map(pairs);
const value = map.get(key);

Options ​

json
{
  "@studnicky/prefer-collection-types": [
    "warn",
    {
      "checkArrayLiterals": true,
      "checkFromEntries": true,
      "checkModuleScopeArrays": true
    }
  ]
}
OptionTypeDefaultDescription
checkArrayLiteralsbooleantrueFlag inline array literals used with .includes() or membership-style indexOf() comparisons (Patterns A and D).
checkFromEntriesbooleantrueFlag direct and const-bound Object.fromEntries() results accessed with computed bracket notation (Pattern B).
checkModuleScopeArraysbooleantrueFlag const arrays used exclusively for .includes() or membership-style indexOf() tests (Pattern C).