Skip to content

Commit fb52f78

Browse files
authored
feat(eslint-plugin): [no-dynamic-delete] allow all string literals as index (typescript-eslint#9280)
* feat(eslint-plugin): [no-dynamic-delete] allow all string literals as index * Update tests * Bleh
1 parent f2f1546 commit fb52f78

File tree

4 files changed

+48
-66
lines changed

4 files changed

+48
-66
lines changed

packages/eslint-plugin/docs/rules/no-dynamic-delete.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ Dynamically adding and removing keys from objects can cause occasional edge case
2525
<TabItem value="❌ Incorrect">
2626

2727
```ts
28-
// Can be replaced with the constant equivalents, such as container.aaa
29-
delete container['aaa'];
30-
delete container['Infinity'];
31-
3228
// Dynamic, difficult-to-reason-about lookups
3329
const name = 'name';
3430
delete container[name];
@@ -48,7 +44,12 @@ delete container.aaa;
4844

4945
// Constants that must be accessed by []
5046
delete container[7];
51-
delete container['-Infinity'];
47+
delete container[-1];
48+
49+
// All strings are allowed, to be compatible with the noPropertyAccessFromIndexSignature
50+
// TS compiler option
51+
delete container['aaa'];
52+
delete container['Infinity'];
5253
```
5354

5455
</TabItem>

packages/eslint-plugin/src/rules/no-dynamic-delete.ts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
22
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
3-
import * as tsutils from 'ts-api-utils';
43

54
import { createRule, nullThrows, NullThrowsReasons } from '../util';
65

@@ -42,9 +41,7 @@ export default createRule({
4241
if (
4342
node.argument.type !== AST_NODE_TYPES.MemberExpression ||
4443
!node.argument.computed ||
45-
isNecessaryDynamicAccess(
46-
diveIntoWrapperExpressions(node.argument.property),
47-
)
44+
isAcceptableIndexExpression(node.argument.property)
4845
) {
4946
return;
5047
}
@@ -80,27 +77,13 @@ export default createRule({
8077
},
8178
});
8279

83-
function diveIntoWrapperExpressions(
84-
node: TSESTree.Expression,
85-
): TSESTree.Expression {
86-
if (node.type === AST_NODE_TYPES.UnaryExpression) {
87-
return diveIntoWrapperExpressions(node.argument);
88-
}
89-
90-
return node;
91-
}
92-
93-
function isNecessaryDynamicAccess(property: TSESTree.Expression): boolean {
94-
if (property.type !== AST_NODE_TYPES.Literal) {
95-
return false;
96-
}
97-
98-
if (typeof property.value === 'number') {
99-
return true;
100-
}
101-
80+
function isAcceptableIndexExpression(property: TSESTree.Expression): boolean {
10281
return (
103-
typeof property.value === 'string' &&
104-
!tsutils.isValidPropertyAccess(property.value)
82+
(property.type === AST_NODE_TYPES.Literal &&
83+
['string', 'number'].includes(typeof property.value)) ||
84+
(property.type === AST_NODE_TYPES.UnaryExpression &&
85+
property.operator === '-' &&
86+
property.argument.type === AST_NODE_TYPES.Literal &&
87+
typeof property.argument.value === 'number')
10588
);
10689
}

packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-dynamic-delete.shot

Lines changed: 6 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/eslint-plugin/tests/rules/no-dynamic-delete.test.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ delete container[-7];
3333
`,
3434
`
3535
const container: { [i: string]: 0 } = {};
36-
delete container[+7];
37-
`,
38-
`
39-
const container: { [i: string]: 0 } = {};
4036
delete container['-Infinity'];
4137
`,
4238
`
@@ -51,19 +47,20 @@ delete value;
5147
const value = 1;
5248
delete -value;
5349
`,
54-
],
55-
invalid: [
56-
{
57-
code: `
50+
`
5851
const container: { [i: string]: 0 } = {};
5952
delete container['aaa'];
60-
`,
61-
errors: [{ messageId: 'dynamicDelete' }],
62-
output: `
53+
`,
54+
`
6355
const container: { [i: string]: 0 } = {};
64-
delete container.aaa;
65-
`,
66-
},
56+
delete container['delete'];
57+
`,
58+
`
59+
const container: { [i: string]: 0 } = {};
60+
delete container['NaN'];
61+
`,
62+
],
63+
invalid: [
6764
{
6865
code: `
6966
const container: { [i: string]: 0 } = {};
@@ -75,13 +72,10 @@ delete container['aa' + 'b'];
7572
{
7673
code: `
7774
const container: { [i: string]: 0 } = {};
78-
delete container['delete'];
75+
delete container[+7];
7976
`,
8077
errors: [{ messageId: 'dynamicDelete' }],
81-
output: `
82-
const container: { [i: string]: 0 } = {};
83-
delete container.delete;
84-
`,
78+
output: null,
8579
},
8680
{
8781
code: `
@@ -110,37 +104,42 @@ delete container[NaN];
110104
{
111105
code: `
112106
const container: { [i: string]: 0 } = {};
113-
delete container['NaN'];
107+
const name = 'name';
108+
delete container[name];
114109
`,
115110
errors: [{ messageId: 'dynamicDelete' }],
116-
output: `
111+
output: null,
112+
},
113+
{
114+
code: `
117115
const container: { [i: string]: 0 } = {};
118-
delete container.NaN;
116+
const getName = () => 'aaa';
117+
delete container[getName()];
119118
`,
119+
output: null,
120+
errors: [{ messageId: 'dynamicDelete' }],
120121
},
121122
{
122123
code: `
123124
const container: { [i: string]: 0 } = {};
124-
const name = 'name';
125-
delete container[name];
125+
const name = { foo: { bar: 'bar' } };
126+
delete container[name.foo.bar];
126127
`,
127-
errors: [{ messageId: 'dynamicDelete' }],
128128
output: null,
129+
errors: [{ messageId: 'dynamicDelete' }],
129130
},
130131
{
131132
code: `
132133
const container: { [i: string]: 0 } = {};
133-
const getName = () => 'aaa';
134-
delete container[getName()];
134+
delete container[+'Infinity'];
135135
`,
136136
output: null,
137137
errors: [{ messageId: 'dynamicDelete' }],
138138
},
139139
{
140140
code: `
141141
const container: { [i: string]: 0 } = {};
142-
const name = { foo: { bar: 'bar' } };
143-
delete container[name.foo.bar];
142+
delete container[typeof 1];
144143
`,
145144
output: null,
146145
errors: [{ messageId: 'dynamicDelete' }],

0 commit comments

Comments
 (0)