Skip to content

Commit 4406aba

Browse files
SyMindljharb
andcommitted
[Fix] no-array-index-key: support optional chaining
Fixes #2896. Co-authored-by: Jordan Harband <[email protected]>
1 parent 2201f9d commit 4406aba

File tree

4 files changed

+64
-17
lines changed

4 files changed

+64
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
99
* [`jsx-no-constructed-context-values`]: avoid a crash with `as X` TS code ([#2894][] @ljharb)
1010
* [`jsx-no-constructed-context-values`]: avoid a crash with boolean shorthand ([#2895][] @ljharb)
1111
* [`static-property-placement`]: do not report non-components ([#2893][] @golopot)
12+
* [ `no-array-index-key`]: support optional chaining ([#2897][] @SyMind)
1213

14+
[#2897]: https://github.com/yannickcr/eslint-plugin-react/pull/2897
1315
[#2895]: https://github.com/yannickcr/eslint-plugin-react/issues/2895
1416
[#2894]: https://github.com/yannickcr/eslint-plugin-react/issues/2894
1517
[#2893]: https://github.com/yannickcr/eslint-plugin-react/pull/2893

lib/rules/no-array-index-key.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ module.exports = {
7777

7878
function getMapIndexParamName(node) {
7979
const callee = node.callee;
80-
if (callee.type !== 'MemberExpression') {
80+
if (callee.type !== 'MemberExpression' && callee.type !== 'OptionalMemberExpression') {
8181
return null;
8282
}
8383
if (callee.property.type !== 'Identifier') {
@@ -153,11 +153,20 @@ module.exports = {
153153
}
154154
}
155155

156+
function popIndex(node) {
157+
const mapIndexParamName = getMapIndexParamName(node);
158+
if (!mapIndexParamName) {
159+
return;
160+
}
161+
162+
indexParamNames.pop();
163+
}
164+
156165
return {
157-
CallExpression(node) {
166+
'CallExpression, OptionalCallExpression'(node) {
158167
if (
159168
node.callee
160-
&& node.callee.type === 'MemberExpression'
169+
&& (node.callee.type === 'MemberExpression' || node.callee.type === 'OptionalMemberExpression')
161170
&& ['createElement', 'cloneElement'].indexOf(node.callee.property.name) !== -1
162171
&& node.arguments.length > 1
163172
) {
@@ -213,14 +222,8 @@ module.exports = {
213222
checkPropValue(value.expression);
214223
},
215224

216-
'CallExpression:exit'(node) {
217-
const mapIndexParamName = getMapIndexParamName(node);
218-
if (!mapIndexParamName) {
219-
return;
220-
}
221-
222-
indexParamNames.pop();
223-
}
225+
'CallExpression:exit': popIndex,
226+
'OptionalCallExpression:exit': popIndex
224227
};
225228
}
226229
};

tests/helpers/parsers.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,11 @@ module.exports = {
1515
return tests;
1616
}
1717
return [];
18+
},
19+
ES2020: function ES2020(tests) {
20+
if (semver.satisfies(version, '>= 6')) {
21+
return tests;
22+
}
23+
return [];
1824
}
1925
};

tests/lib/rules/no-array-index-key.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// -----------------------------------------------------------------------------
1111

1212
const RuleTester = require('eslint').RuleTester;
13+
const parsers = require('../../helpers/parsers');
1314
const rule = require('../../../lib/rules/no-array-index-key');
1415

1516
const parserOptions = {
@@ -26,7 +27,7 @@ const parserOptions = {
2627

2728
const ruleTester = new RuleTester({parserOptions});
2829
ruleTester.run('no-array-index-key', rule, {
29-
valid: [
30+
valid: [].concat(
3031
{code: '<Foo key="foo" />;'},
3132
{code: '<Foo key={i} />;'},
3233
{code: '<Foo key />;'},
@@ -105,10 +106,26 @@ ruleTester.run('no-array-index-key', rule, {
105106
return React.cloneElement(child, { key: child.id });
106107
})
107108
`
108-
}
109-
],
109+
},
110110

111-
invalid: [
111+
parsers.ES2020({
112+
code: 'foo?.map(child => <Foo key={child.i} />)',
113+
parserOptions: {
114+
ecmaVersion: 2020
115+
}
116+
}, {
117+
code: 'foo?.map(child => <Foo key={child.i} />)',
118+
parser: parsers.BABEL_ESLINT
119+
}, {
120+
code: 'foo?.map(child => <Foo key={child.i} />)',
121+
parser: parsers.TYPESCRIPT_ESLINT
122+
}, {
123+
code: 'foo?.map(child => <Foo key={child.i} />)',
124+
parser: parsers['@TYPESCRIPT_ESLINT']
125+
})
126+
),
127+
128+
invalid: [].concat(
112129
{
113130
code: 'foo.map((bar, i) => <Foo key={i} />)',
114131
errors: [{message: 'Do not use Array index in keys'}]
@@ -279,7 +296,26 @@ ruleTester.run('no-array-index-key', rule, {
279296
})
280297
`,
281298
errors: [{message: 'Do not use Array index in keys'}]
282-
}
299+
},
283300

284-
]
301+
parsers.ES2020({
302+
code: 'foo?.map((child, i) => <Foo key={i} />)',
303+
errors: [{message: 'Do not use Array index in keys'}],
304+
parserOptions: {
305+
ecmaVersion: 2020
306+
}
307+
}, {
308+
code: 'foo?.map((child, i) => <Foo key={i} />)',
309+
errors: [{message: 'Do not use Array index in keys'}],
310+
parser: parsers.BABEL_ESLINT
311+
}, {
312+
code: 'foo?.map((child, i) => <Foo key={i} />)',
313+
errors: [{message: 'Do not use Array index in keys'}],
314+
parser: parsers.TYPESCRIPT_ESLINT
315+
}, {
316+
code: 'foo?.map((child, i) => <Foo key={i} />)',
317+
errors: [{message: 'Do not use Array index in keys'}],
318+
parser: parsers['@TYPESCRIPT_ESLINT']
319+
})
320+
)
285321
});

0 commit comments

Comments
 (0)