Skip to content

Commit

Permalink
basic impl of typescript-eslint#8979
Browse files Browse the repository at this point in the history
  • Loading branch information
kirkwaiblinger committed May 3, 2024
1 parent 9cb9a4d commit 15406c4
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 14 deletions.
34 changes: 20 additions & 14 deletions packages/eslint-plugin/src/rules/prefer-array-at.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { TSESTree } from '@typescript-eslint/utils';
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import { isIntrinsicNumberType } from 'ts-api-utils';
import { isIntrinsicNumberType, unionTypeParts } from 'ts-api-utils';

import {
createRule,
getConstrainedTypeAtLocation,
getParserServices,
getWrappingFixer,
nullThrows,
} from '../util';

Expand Down Expand Up @@ -44,11 +45,14 @@ export default createRule({
// 2. replaces the index access with the at method of the same value

const { expression, typeAnnotation } = node;
if (expression.type !== AST_NODE_TYPES.MemberExpression) {
let memberExpression = expression;
while (memberExpression.type === AST_NODE_TYPES.ChainExpression) {
memberExpression = memberExpression.expression;
}
if (memberExpression.type !== AST_NODE_TYPES.MemberExpression) {
return;
}

const memberExpression = expression;
if (!memberExpression.computed || memberExpression.optional) {
return;
}
Expand All @@ -74,8 +78,14 @@ export default createRule({
return;
}

const indexType = getConstrainedTypeAtLocation(services, index);
if (
!isIntrinsicNumberType(getConstrainedTypeAtLocation(services, index))
!(
isIntrinsicNumberType(indexType) ||
unionTypeParts(indexType).every(unionPart =>
unionPart.isNumberLiteral(),
)
)
) {
return;
}
Expand Down Expand Up @@ -108,16 +118,12 @@ export default createRule({
suggest: [
{
messageId: 'useArrayAtSuggestion',
fix: fixer => {
const indexText = context.sourceCode.getText(index);
const objectText = context.sourceCode.getText(
memberExpression.object,
);
return fixer.replaceText(
node,
`${objectText}.at(${indexText})`,
);
},
fix: getWrappingFixer({
sourceCode: context.sourceCode,
node,
innerNode: [memberExpression.object, index],
wrap: (object, index) => `${object}.at(${index})`,
}),
},
],
});
Expand Down
148 changes: 148 additions & 0 deletions packages/eslint-plugin/tests/rules/prefer-array-at.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ ruleTester.run('prefer-array-at', rule, {
declare const nums: Array<number>;
const maybeNum = nums['length'] as number | undefined;
`,
`
declare const index: number;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray.array?.[index] as undefined | number;
`,
`
declare const index: 12 | 13 | 'length';
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray?.array[index] as undefined | number;
`,
],
invalid: [
{
Expand Down Expand Up @@ -67,5 +81,139 @@ const maybeNum = nums.at(index);
},
],
},
{
code: `
declare const index: number;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray.array[index] as undefined | number;
`,
errors: [
{
messageId: 'preferArrayAt',
line: 6,
suggestions: [
{
messageId: 'useArrayAtSuggestion',
output: `
declare const index: number;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray.array.at(index);
`,
},
],
},
],
},
{
code: `
declare const index: number;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray?.array[index] as undefined | number;
`,
errors: [
{
messageId: 'preferArrayAt',
line: 6,
suggestions: [
{
messageId: 'useArrayAtSuggestion',
output: `
declare const index: number;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray?.array.at(index);
`,
},
],
},
],
},
{
code: `
declare const index: number;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray?.array[12] as undefined | number;
`,
errors: [
{
messageId: 'preferArrayAt',
line: 6,
suggestions: [
{
messageId: 'useArrayAtSuggestion',
output: `
declare const index: number;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray?.array.at(12);
`,
},
],
},
],
},
{
code: `
declare const index: 12 | 13;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray?.array[index] as undefined | number;
`,
errors: [
{
messageId: 'preferArrayAt',
line: 6,
suggestions: [
{
messageId: 'useArrayAtSuggestion',
output: `
declare const index: 12 | 13;
const objectWithArray = {
array: [1, 2, 3],
};
const maybeNum = objectWithArray?.array.at(index);
`,
},
],
},
],
},
{
code: `
declare const arr1: Array<number>;
declare const arr2: Array<number>;
declare const index: number;
const maybeNum = (Math.random() > 0.5 ? arr1 : arr2)[index] as
| number
| undefined;
`,
errors: [
{
messageId: 'preferArrayAt',
suggestions: [
{
messageId: 'useArrayAtSuggestion',
output: `
declare const arr1: Array<number>;
declare const arr2: Array<number>;
declare const index: number;
const maybeNum = (Math.random() > 0.5 ? arr1 : arr2).at(index);
`,
},
],
},
],
},
],
});

0 comments on commit 15406c4

Please sign in to comment.