Function Reference In Iterator
ID |
javascript.function_reference_in_iterator |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
JavaScript |
Tags |
arrays, callbacks, reliability |
Description
Reports a bare function reference passed to an array iterator (map, filter, forEach, flatMap, some, every, find, findIndex, findLast, findLastIndex) that will hand it more arguments than the reference is written to take.
Two shapes are reported, and only two, because they are the ones where the extra arguments are certainly unwanted:
-
a global whose extra parameter is not an index —
parseIntby default, extendable through theglobalsWithExtraArgumentsproperty; -
a function the file declares with more than three parameters, since an iterator supplies at most three and the rest can only ever be
undefined.
A declared function of two or three parameters is not reported: writing function label(value, index) and passing it to map is the ordinary way to use the index, and nothing in the source separates that from a mistake. A rest parameter is not reported either — a variadic function is written to take whatever it is given.
Only a bare name is examined. An inline arrow states its own parameter list at the call site, and a reference reached through a property (obj.fn, Number.parseInt) resolves to nothing whose parameters can be counted.
Rationale
// Bad
const ids = ['1', '2', '3'].map(parseInt); // [1, NaN, NaN]
-
map,filter,forEachand their relatives call the callback with three arguments — the element, its index and the array — while the code reads as if only the element were passed. -
parseIntis the textbook case: its second parameter is a radix, so the iterator’s index arrives as one.parseInt('2', 1)isNaNbecause base 1 does not exist, andparseInt('3', 2)isNaNbecause 3 is not a binary digit. -
The array comes out wrong with no error anywhere — and it is right for element zero, which is what a quick check in the console usually looks at.
Remediation
Pass a callback that forwards only the arguments the function is written to take.
// Good
const ids = ['1', '2', '3'].map(value => parseInt(value, 10));
// Good — a single-parameter function is safe to pass by name
const numbers = ['1', '2', '3'].map(Number);
const present = values.filter(Boolean);
Configuration
The globals whose next parameter means something other than an index are configurable through
globalsWithExtraArguments. The default holds parseInt; add any project-level function that is
reachable without a declaration in the file and takes more than the one argument callers expect:
properties:
globalsWithExtraArguments:
- parseInt
- formatWithLocale