diff --git a/packages/eslint-plugin/src/rules/prefer-find.ts b/packages/eslint-plugin/src/rules/prefer-find.ts index babee93bab8..154d27d1a86 100644 --- a/packages/eslint-plugin/src/rules/prefer-find.ts +++ b/packages/eslint-plugin/src/rules/prefer-find.ts @@ -125,7 +125,7 @@ export default createRule({ return isAtLeastOneArrayishComponent; } - function getObjectIfArrayAtExpression( + function getObjectIfArrayAtZeroExpression( node: TSESTree.CallExpression, ): TSESTree.Expression | undefined { // .at() should take exactly one argument. @@ -133,14 +133,14 @@ export default createRule({ return undefined; } - const atArgument = getStaticValue(node.arguments[0], globalScope); - if (atArgument != null && isTreatedAsZeroByArrayAt(atArgument.value)) { - const callee = node.callee; - if ( - callee.type === AST_NODE_TYPES.MemberExpression && - !callee.optional && - isStaticMemberAccessOfValue(callee, 'at', globalScope) - ) { + const callee = node.callee; + if ( + callee.type === AST_NODE_TYPES.MemberExpression && + !callee.optional && + isStaticMemberAccessOfValue(callee, 'at', globalScope) + ) { + const atArgument = getStaticValue(node.arguments[0], globalScope); + if (atArgument != null && isTreatedAsZeroByArrayAt(atArgument.value)) { return callee.object; } } @@ -153,6 +153,12 @@ export default createRule({ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at#parameters */ function isTreatedAsZeroByArrayAt(value: unknown): boolean { + // This would cause the number constructor coercion to throw. Other static + // values are safe. + if (typeof value === 'symbol') { + return false; + } + const asNumber = Number(value); if (isNaN(asNumber)) { @@ -215,7 +221,7 @@ export default createRule({ return { // This query will be used to find things like `filteredResults.at(0)`. CallExpression(node): void { - const object = getObjectIfArrayAtExpression(node); + const object = getObjectIfArrayAtZeroExpression(node); if (object) { const filterExpression = parseIfArrayFilterExpression(object); if (filterExpression) { diff --git a/packages/eslint-plugin/tests/rules/prefer-find.test.ts b/packages/eslint-plugin/tests/rules/prefer-find.test.ts index 6b303ea9aed..dddb56f14d3 100644 --- a/packages/eslint-plugin/tests/rules/prefer-find.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-find.test.ts @@ -56,6 +56,20 @@ ruleTester.run('prefer-find', rule, { "['Just', 'a', 'find'].find(x => x.length > 4);", 'undefined.filter(x => x)[0];', 'null?.filter(x => x)[0];', + // Should not throw. See https://github.com/typescript-eslint/typescript-eslint/issues/8386 + ` + declare function foo(param: any): any; + foo(Symbol.for('foo')); + `, + // Specifically need to test Symbol.for(), not just Symbol(), since only + // Symbol.for() creates a static value that the rule inspects. + ` + declare const arr: string[]; + const s = Symbol.for("Don't throw!"); + arr.filter(item => item === 'aha').at(s); + `, + "[1, 2, 3].filter(x => x)[Symbol('0')];", + "[1, 2, 3].filter(x => x)[Symbol.for('0')];", ], invalid: [