A footgun is a feature of a product that makes it easy to shoot yourself in the foot. Here are the most common ones I have encountered in JavaScript:

1. Mapping over functions by name instead of explicitly passing them their arguments:

What does ["1", "2", "3"].map(parseInt) return? The answer is [ 1, NaN, NaN ]. Why? Because parseInt receives two arguments: the string to parse and the radix (base) in which to parse it. Array.prototype.map passes three arguments to the callback function: the current element, the index of the current element, and the array itself. So the code above is actually equivalent to ["1", "2", "3"].map((element, index, array) => parseInt(element, index)). The moral of the story is: always pass the function you want to map to map as an arrow function, not by name. Even if the function you are using only takes one argument, someone might add a second argument to it in the future, and then your code will break.

2. Checking the truthiness of a promise:

What does if (promise) { ... } do? The answer is: it always executes the code in the if block. Why? Because a promise is an object, and all objects are truthy. The moral of the story is: if you want to check whether a promise has resolved, you must await it. E.g. if (await promise) { ... }. This situation can be avoided by enabling the @typescript-eslint/no-misused-promises linter rule.

3. Expecting Array.prototype.splice to behave like Array.prototype.slice:

Array.prototype.slice returns a new array containing the elements of the original array from the start index to the end index. The original array is not modified. Array.prototype.splice, on the other hand, is a mutative operation, it modifies the original array and returns the removed elements, NOT the remaining elements.

© Oli Wales 2024