Skip to content

Commit e6074fe

Browse files
authoredFeb 19, 2024
prefer-array-find: Check array.filter().at(0) (#2284)
1 parent 5ce4169 commit e6074fe

File tree

2 files changed

+116
-1
lines changed

2 files changed

+116
-1
lines changed
 

‎rules/prefer-array-find.js

+28
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const {isMethodCall} = require('./ast/index.js');
1818
const ERROR_ZERO_INDEX = 'error-zero-index';
1919
const ERROR_SHIFT = 'error-shift';
2020
const ERROR_POP = 'error-pop';
21+
const ERROR_AT_ZERO = 'error-at-zero';
2122
const ERROR_AT_MINUS_ONE = 'error-at-minus-one';
2223
const ERROR_DESTRUCTURING_DECLARATION = 'error-destructuring-declaration';
2324
const ERROR_DESTRUCTURING_ASSIGNMENT = 'error-destructuring-assignment';
@@ -27,6 +28,7 @@ const SUGGESTION_LOGICAL_OR_OPERATOR = 'suggest-logical-or-operator';
2728
const messages = {
2829
[ERROR_DECLARATION]: 'Prefer `.find(…)` over `.filter(…)`.',
2930
[ERROR_ZERO_INDEX]: 'Prefer `.find(…)` over `.filter(…)[0]`.',
31+
[ERROR_AT_ZERO]: 'Prefer `.find(…)` over `.filter(…).at(0)`.',
3032
[ERROR_SHIFT]: 'Prefer `.find(…)` over `.filter(…).shift()`.',
3133
[ERROR_POP]: 'Prefer `.findLast(…)` over `.filter(…).pop()`.',
3234
[ERROR_AT_MINUS_ONE]: 'Prefer `.findLast(…)` over `.filter(…).at(-1)`.',
@@ -335,6 +337,32 @@ const create = context => {
335337
return problem;
336338
});
337339

340+
// `array.filter().at(0)`
341+
context.on('CallExpression', node => {
342+
if (!(
343+
isMethodCall(node, {
344+
method: 'at',
345+
argumentsLength: 1,
346+
optionalCall: false,
347+
optionalMember: false,
348+
})
349+
&& node.arguments[0].type === 'Literal'
350+
&& node.arguments[0].raw === '0'
351+
&& isArrayFilterCall(node.callee.object)
352+
)) {
353+
return;
354+
}
355+
356+
return {
357+
node: node.callee.object.callee.property,
358+
messageId: ERROR_AT_ZERO,
359+
fix: fixer => [
360+
fixer.replaceText(node.callee.object.callee.property, 'find'),
361+
...removeMethodCall(fixer, node, sourceCode),
362+
],
363+
};
364+
});
365+
338366
if (!checkFromLast) {
339367
return;
340368
}

‎test/prefer-array-find.mjs

+88-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {test} = getTester(import.meta);
66
const ERROR_ZERO_INDEX = 'error-zero-index';
77
const ERROR_SHIFT = 'error-shift';
88
const ERROR_POP = 'error-pop';
9+
const ERROR_AT_ZERO = 'error-at-zero';
910
const ERROR_AT_MINUS_ONE = 'error-at-minus-one';
1011
const ERROR_DESTRUCTURING_DECLARATION = 'error-destructuring-declaration';
1112
const ERROR_DESTRUCTURING_ASSIGNMENT = 'error-destructuring-assignment';
@@ -1030,7 +1031,7 @@ test({
10301031
'array.filter(foo).at(1)',
10311032
'array.filter(foo).at(+1)',
10321033
'const ONE = 1; array.filter(foo).at(-ONE)',
1033-
'const MINUS_ONE = 1; array.filter(foo).at(MINUS_ONE)',
1034+
'const MINUS_ONE = -1; array.filter(foo).at(MINUS_ONE)',
10341035
'const a = {b: 1}; array.filter(foo).at(-a.b)',
10351036
'const a = {b: -1}; array.filter(foo).at(a.b)',
10361037
'array.filter(foo).at(-2)',
@@ -1100,3 +1101,89 @@ test({
11001101
},
11011102
].map(test => ({...test, options: checkFromLastOptions})),
11021103
});
1104+
1105+
// `.at(0)`
1106+
test({
1107+
valid: [
1108+
// Test `.at()`
1109+
// Not `CallExpression`
1110+
'array2.filter(foo).at',
1111+
// Not `MemberExpression`
1112+
'at(array.filter(foo), 0)',
1113+
// `callee.property` is not a `Identifier`
1114+
'array.filter(foo)["at"](0)',
1115+
// Computed
1116+
'array.filter(foo)[at](0)',
1117+
// Not `at`
1118+
'array.filter(foo).notAt(0)',
1119+
// More or less argument(s)
1120+
'array2.filter(foo).at()',
1121+
'array.filter(foo).at(0, extraArgument)',
1122+
'array.filter(foo).at(...[0])',
1123+
1124+
// Test `0`
1125+
'array.filter(foo).at(100)',
1126+
'array.filter(foo).at(+0)',
1127+
'const ZERO = 0; array.filter(foo).at(ZERO)',
1128+
'const a = {b: 0}; array.filter(foo).at(a.b)',
1129+
'array.filter(foo).at(0b0)',
1130+
'array.filter(foo).at("0")',
1131+
1132+
// Test `.filter()`
1133+
// Not `CallExpression`
1134+
'array.filter.at(0)',
1135+
// Not `MemberExpression`
1136+
'filter(foo).at(0)',
1137+
// `callee.property` is not a `Identifier`
1138+
'array["filter"](foo).at(0)',
1139+
// Computed
1140+
'array[filter](foo).at(0)',
1141+
// Not `filter`
1142+
'array.notFilter(foo).at(0)',
1143+
// More or less argument(s)
1144+
'array.filter().at(0)',
1145+
'array.filter(foo, thisArgument, extraArgument).at(0)',
1146+
'array.filter(...foo).at(0)',
1147+
],
1148+
invalid: [
1149+
{
1150+
code: 'array.filter(foo).at(0)',
1151+
output: 'array.find(foo)',
1152+
errors: [{messageId: ERROR_AT_ZERO}],
1153+
},
1154+
{
1155+
code: 'array.filter(foo, thisArgument).at(0)',
1156+
output: 'array.find(foo, thisArgument)',
1157+
errors: [{messageId: ERROR_AT_ZERO}],
1158+
},
1159+
{
1160+
code: outdent`
1161+
const item = array
1162+
// comment 1
1163+
.filter(
1164+
// comment 2
1165+
x => x === '🦄'
1166+
)
1167+
// comment 3
1168+
.at(
1169+
// comment 4
1170+
0
1171+
// comment 5
1172+
)
1173+
// comment 6
1174+
;
1175+
`,
1176+
output: outdent`
1177+
const item = array
1178+
// comment 1
1179+
.find(
1180+
// comment 2
1181+
x => x === '🦄'
1182+
)
1183+
// comment 6
1184+
;
1185+
`,
1186+
errors: [{messageId: ERROR_AT_ZERO}],
1187+
},
1188+
],
1189+
});

0 commit comments

Comments
 (0)
Please sign in to comment.