eslint-config-typed 4.9.1 → 4.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugins/ts-restrictions/rules/check-destructuring-completeness.d.mts.map +1 -1
- package/dist/plugins/ts-restrictions/rules/check-destructuring-completeness.mjs +59 -50
- package/dist/plugins/ts-restrictions/rules/check-destructuring-completeness.mjs.map +1 -1
- package/package.json +1 -1
- package/src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts +75 -68
- package/src/plugins/ts-restrictions/rules/check-destructuring-completeness.test.mts +95 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-destructuring-completeness.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAGlC,KAAK,OAAO,GAAG,SAAS;IACtB,QAAQ,CAAC;QACP,8BAA8B,CAAC,EAAE,OAAO,CAAC;QACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,UAAU,GAAG,yBAAyB,CAAC;AAI5C,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,UAAU,CAC9D,UAAU,EACV,OAAO,
|
|
1
|
+
{"version":3,"file":"check-destructuring-completeness.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAGlC,KAAK,OAAO,GAAG,SAAS;IACtB,QAAQ,CAAC;QACP,8BAA8B,CAAC,EAAE,OAAO,CAAC;QACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,UAAU,GAAG,yBAAyB,CAAC;AAI5C,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,UAAU,CAC9D,UAAU,EACV,OAAO,CAwMC,CAAC"}
|
|
@@ -90,38 +90,27 @@ const checkDestructuringCompleteness = {
|
|
|
90
90
|
}
|
|
91
91
|
return false;
|
|
92
92
|
};
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
return;
|
|
96
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
97
|
-
if (node.init === undefined || node.init === null)
|
|
98
|
-
return;
|
|
99
|
-
const shouldCheck = hasDirectiveComment(node) || isReactComponentPropsDestructuring(node);
|
|
100
|
-
if (!shouldCheck)
|
|
101
|
-
return;
|
|
102
|
-
// If rest element is used, skip completeness check
|
|
103
|
-
const hasRestElement = node.id.properties.some((prop) => prop.type === AST_NODE_TYPES.RestElement);
|
|
93
|
+
const checkObjectPatternCompleteness = (objectPattern, tsSourceNode) => {
|
|
94
|
+
const hasRestElement = objectPattern.properties.some((prop) => prop.type === AST_NODE_TYPES.RestElement);
|
|
104
95
|
if (hasRestElement)
|
|
105
96
|
return;
|
|
106
97
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
107
|
-
const tsNode = esTreeNodeToTSNodeMap.get(
|
|
98
|
+
const tsNode = esTreeNodeToTSNodeMap.get(tsSourceNode);
|
|
108
99
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
109
100
|
if (tsNode === undefined)
|
|
110
101
|
return;
|
|
111
102
|
const type = typeChecker.getTypeAtLocation(tsNode);
|
|
112
103
|
const objectProps = getObjectTypeProperties(type);
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
const missingProps = objectProps.filter((prop) => !mut_destructuredProps.has(prop));
|
|
104
|
+
const collected = collectDestructuredPropNames(objectPattern.properties);
|
|
105
|
+
// Dynamic computed properties cannot be resolved statically,
|
|
106
|
+
// so skip completeness check when they are present.
|
|
107
|
+
if (collected.hasDynamicComputedKey)
|
|
108
|
+
return;
|
|
109
|
+
const missingProps = objectProps.filter((prop) => !collected.names.has(prop));
|
|
121
110
|
if (missingProps.length > 0) {
|
|
122
111
|
context.report({
|
|
123
112
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
124
|
-
node:
|
|
113
|
+
node: objectPattern,
|
|
125
114
|
messageId: 'incompleteDestructuring',
|
|
126
115
|
data: {
|
|
127
116
|
missingProps: missingProps.join(', '),
|
|
@@ -130,7 +119,17 @@ const checkDestructuringCompleteness = {
|
|
|
130
119
|
}
|
|
131
120
|
};
|
|
132
121
|
return {
|
|
133
|
-
VariableDeclarator:
|
|
122
|
+
VariableDeclarator: (node) => {
|
|
123
|
+
if (node.id.type !== AST_NODE_TYPES.ObjectPattern)
|
|
124
|
+
return;
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
126
|
+
if (node.init === undefined || node.init === null)
|
|
127
|
+
return;
|
|
128
|
+
const shouldCheck = hasDirectiveComment(node) || isReactComponentPropsDestructuring(node);
|
|
129
|
+
if (!shouldCheck)
|
|
130
|
+
return;
|
|
131
|
+
checkObjectPatternCompleteness(node.id, node.init);
|
|
132
|
+
},
|
|
134
133
|
ArrowFunctionExpression: (node) => {
|
|
135
134
|
if (!alwaysCheckReactComponentProps)
|
|
136
135
|
return;
|
|
@@ -138,34 +137,7 @@ const checkDestructuringCompleteness = {
|
|
|
138
137
|
return;
|
|
139
138
|
for (const param of node.params) {
|
|
140
139
|
if (param.type === AST_NODE_TYPES.ObjectPattern) {
|
|
141
|
-
|
|
142
|
-
const hasRestElement = param.properties.some((prop) => prop.type === AST_NODE_TYPES.RestElement);
|
|
143
|
-
if (hasRestElement)
|
|
144
|
-
continue;
|
|
145
|
-
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
146
|
-
const tsNode = esTreeNodeToTSNodeMap.get(param);
|
|
147
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
148
|
-
if (tsNode === undefined)
|
|
149
|
-
continue;
|
|
150
|
-
const type = typeChecker.getTypeAtLocation(tsNode);
|
|
151
|
-
const objectProps = getObjectTypeProperties(type);
|
|
152
|
-
const mut_destructuredProps = new Set();
|
|
153
|
-
for (const prop of param.properties) {
|
|
154
|
-
if (prop.type === AST_NODE_TYPES.Property &&
|
|
155
|
-
prop.key.type === AST_NODE_TYPES.Identifier) {
|
|
156
|
-
mut_destructuredProps.add(prop.key.name);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const missingProps = objectProps.filter((prop) => !mut_destructuredProps.has(prop));
|
|
160
|
-
if (missingProps.length > 0) {
|
|
161
|
-
context.report({
|
|
162
|
-
node: param,
|
|
163
|
-
messageId: 'incompleteDestructuring',
|
|
164
|
-
data: {
|
|
165
|
-
missingProps: missingProps.join(', '),
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
}
|
|
140
|
+
checkObjectPatternCompleteness(param, param);
|
|
169
141
|
}
|
|
170
142
|
}
|
|
171
143
|
},
|
|
@@ -195,6 +167,43 @@ const getObjectTypeProperties = (type) => {
|
|
|
195
167
|
return [];
|
|
196
168
|
}
|
|
197
169
|
};
|
|
170
|
+
const collectDestructuredPropNames = (properties) => {
|
|
171
|
+
const mut_names = new Set();
|
|
172
|
+
let mut_hasDynamicComputedKey = false;
|
|
173
|
+
for (const prop of properties) {
|
|
174
|
+
if (prop.type !== AST_NODE_TYPES.Property)
|
|
175
|
+
continue;
|
|
176
|
+
if (!prop.computed) {
|
|
177
|
+
switch (prop.key.type) {
|
|
178
|
+
case AST_NODE_TYPES.Identifier: {
|
|
179
|
+
mut_names.add(prop.key.name);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case AST_NODE_TYPES.Literal: {
|
|
183
|
+
if (typeof prop.key.value === 'string') {
|
|
184
|
+
mut_names.add(prop.key.value);
|
|
185
|
+
}
|
|
186
|
+
else if (typeof prop.key.value === 'number') {
|
|
187
|
+
mut_names.add(String(prop.key.value));
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else if (prop.key.type === AST_NODE_TYPES.Literal &&
|
|
194
|
+
(typeof prop.key.value === 'string' || typeof prop.key.value === 'number')) {
|
|
195
|
+
// Computed key with a static literal value, e.g. { ['data-id']: val }
|
|
196
|
+
mut_names.add(typeof prop.key.value === 'string'
|
|
197
|
+
? prop.key.value
|
|
198
|
+
: String(prop.key.value));
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Dynamic computed key — cannot resolve statically
|
|
202
|
+
mut_hasDynamicComputedKey = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return { names: mut_names, hasDynamicComputedKey: mut_hasDynamicComputedKey };
|
|
206
|
+
};
|
|
198
207
|
const isReactComponentFunction = (node) => {
|
|
199
208
|
if (node === undefined || node === null)
|
|
200
209
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-destructuring-completeness.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"sourcesContent":[null],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"check-destructuring-completeness.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -148,28 +148,18 @@ export const checkDestructuringCompleteness: TSESLint.RuleModule<
|
|
|
148
148
|
return false;
|
|
149
149
|
};
|
|
150
150
|
|
|
151
|
-
const
|
|
152
|
-
|
|
151
|
+
const checkObjectPatternCompleteness = (
|
|
152
|
+
objectPattern: DeepReadonly<TSESTree.ObjectPattern>,
|
|
153
|
+
tsSourceNode: DeepReadonly<TSESTree.Node>,
|
|
153
154
|
): void => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
157
|
-
if (node.init === undefined || node.init === null) return;
|
|
158
|
-
|
|
159
|
-
const shouldCheck =
|
|
160
|
-
hasDirectiveComment(node) || isReactComponentPropsDestructuring(node);
|
|
161
|
-
|
|
162
|
-
if (!shouldCheck) return;
|
|
163
|
-
|
|
164
|
-
// If rest element is used, skip completeness check
|
|
165
|
-
const hasRestElement = node.id.properties.some(
|
|
155
|
+
const hasRestElement = objectPattern.properties.some(
|
|
166
156
|
(prop) => prop.type === AST_NODE_TYPES.RestElement,
|
|
167
157
|
);
|
|
168
158
|
|
|
169
159
|
if (hasRestElement) return;
|
|
170
160
|
|
|
171
161
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
172
|
-
const tsNode = esTreeNodeToTSNodeMap.get(
|
|
162
|
+
const tsNode = esTreeNodeToTSNodeMap.get(tsSourceNode as never);
|
|
173
163
|
|
|
174
164
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
175
165
|
if (tsNode === undefined) return;
|
|
@@ -178,25 +168,20 @@ export const checkDestructuringCompleteness: TSESLint.RuleModule<
|
|
|
178
168
|
|
|
179
169
|
const objectProps = getObjectTypeProperties(type);
|
|
180
170
|
|
|
181
|
-
const
|
|
171
|
+
const collected = collectDestructuredPropNames(objectPattern.properties);
|
|
182
172
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
prop.key.type === AST_NODE_TYPES.Identifier
|
|
187
|
-
) {
|
|
188
|
-
mut_destructuredProps.add(prop.key.name);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
173
|
+
// Dynamic computed properties cannot be resolved statically,
|
|
174
|
+
// so skip completeness check when they are present.
|
|
175
|
+
if (collected.hasDynamicComputedKey) return;
|
|
191
176
|
|
|
192
177
|
const missingProps = objectProps.filter(
|
|
193
|
-
(prop) => !
|
|
178
|
+
(prop) => !collected.names.has(prop),
|
|
194
179
|
);
|
|
195
180
|
|
|
196
181
|
if (missingProps.length > 0) {
|
|
197
182
|
context.report({
|
|
198
183
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
199
|
-
node:
|
|
184
|
+
node: objectPattern as never,
|
|
200
185
|
messageId: 'incompleteDestructuring',
|
|
201
186
|
data: {
|
|
202
187
|
missingProps: missingProps.join(', '),
|
|
@@ -206,7 +191,19 @@ export const checkDestructuringCompleteness: TSESLint.RuleModule<
|
|
|
206
191
|
};
|
|
207
192
|
|
|
208
193
|
return {
|
|
209
|
-
VariableDeclarator:
|
|
194
|
+
VariableDeclarator: (node) => {
|
|
195
|
+
if (node.id.type !== AST_NODE_TYPES.ObjectPattern) return;
|
|
196
|
+
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
198
|
+
if (node.init === undefined || node.init === null) return;
|
|
199
|
+
|
|
200
|
+
const shouldCheck =
|
|
201
|
+
hasDirectiveComment(node) || isReactComponentPropsDestructuring(node);
|
|
202
|
+
|
|
203
|
+
if (!shouldCheck) return;
|
|
204
|
+
|
|
205
|
+
checkObjectPatternCompleteness(node.id, node.init);
|
|
206
|
+
},
|
|
210
207
|
ArrowFunctionExpression: (node) => {
|
|
211
208
|
if (!alwaysCheckReactComponentProps) return;
|
|
212
209
|
|
|
@@ -214,47 +211,7 @@ export const checkDestructuringCompleteness: TSESLint.RuleModule<
|
|
|
214
211
|
|
|
215
212
|
for (const param of node.params) {
|
|
216
213
|
if (param.type === AST_NODE_TYPES.ObjectPattern) {
|
|
217
|
-
|
|
218
|
-
const hasRestElement = param.properties.some(
|
|
219
|
-
(prop) => prop.type === AST_NODE_TYPES.RestElement,
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
if (hasRestElement) continue;
|
|
223
|
-
|
|
224
|
-
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
225
|
-
const tsNode = esTreeNodeToTSNodeMap.get(param as never);
|
|
226
|
-
|
|
227
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
228
|
-
if (tsNode === undefined) continue;
|
|
229
|
-
|
|
230
|
-
const type = typeChecker.getTypeAtLocation(tsNode);
|
|
231
|
-
|
|
232
|
-
const objectProps = getObjectTypeProperties(type);
|
|
233
|
-
|
|
234
|
-
const mut_destructuredProps = new Set<string>();
|
|
235
|
-
|
|
236
|
-
for (const prop of param.properties) {
|
|
237
|
-
if (
|
|
238
|
-
prop.type === AST_NODE_TYPES.Property &&
|
|
239
|
-
prop.key.type === AST_NODE_TYPES.Identifier
|
|
240
|
-
) {
|
|
241
|
-
mut_destructuredProps.add(prop.key.name);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const missingProps = objectProps.filter(
|
|
246
|
-
(prop) => !mut_destructuredProps.has(prop),
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
if (missingProps.length > 0) {
|
|
250
|
-
context.report({
|
|
251
|
-
node: param,
|
|
252
|
-
messageId: 'incompleteDestructuring',
|
|
253
|
-
data: {
|
|
254
|
-
missingProps: missingProps.join(', '),
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
}
|
|
214
|
+
checkObjectPatternCompleteness(param, param);
|
|
258
215
|
}
|
|
259
216
|
}
|
|
260
217
|
},
|
|
@@ -289,6 +246,56 @@ const getObjectTypeProperties = (type: ts.Type): readonly string[] => {
|
|
|
289
246
|
}
|
|
290
247
|
};
|
|
291
248
|
|
|
249
|
+
const collectDestructuredPropNames = (
|
|
250
|
+
properties: DeepReadonly<TSESTree.ObjectPattern['properties']>,
|
|
251
|
+
): Readonly<{
|
|
252
|
+
names: ReadonlySet<string>;
|
|
253
|
+
hasDynamicComputedKey: boolean;
|
|
254
|
+
}> => {
|
|
255
|
+
const mut_names = new Set<string>();
|
|
256
|
+
|
|
257
|
+
let mut_hasDynamicComputedKey = false;
|
|
258
|
+
|
|
259
|
+
for (const prop of properties) {
|
|
260
|
+
if (prop.type !== AST_NODE_TYPES.Property) continue;
|
|
261
|
+
|
|
262
|
+
if (!prop.computed) {
|
|
263
|
+
switch (prop.key.type) {
|
|
264
|
+
case AST_NODE_TYPES.Identifier: {
|
|
265
|
+
mut_names.add(prop.key.name);
|
|
266
|
+
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
case AST_NODE_TYPES.Literal: {
|
|
271
|
+
if (typeof prop.key.value === 'string') {
|
|
272
|
+
mut_names.add(prop.key.value);
|
|
273
|
+
} else if (typeof prop.key.value === 'number') {
|
|
274
|
+
mut_names.add(String(prop.key.value));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} else if (
|
|
281
|
+
prop.key.type === AST_NODE_TYPES.Literal &&
|
|
282
|
+
(typeof prop.key.value === 'string' || typeof prop.key.value === 'number')
|
|
283
|
+
) {
|
|
284
|
+
// Computed key with a static literal value, e.g. { ['data-id']: val }
|
|
285
|
+
mut_names.add(
|
|
286
|
+
typeof prop.key.value === 'string'
|
|
287
|
+
? prop.key.value
|
|
288
|
+
: String(prop.key.value),
|
|
289
|
+
);
|
|
290
|
+
} else {
|
|
291
|
+
// Dynamic computed key — cannot resolve statically
|
|
292
|
+
mut_hasDynamicComputedKey = true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return { names: mut_names, hasDynamicComputedKey: mut_hasDynamicComputedKey };
|
|
297
|
+
};
|
|
298
|
+
|
|
292
299
|
const isReactComponentFunction = (
|
|
293
300
|
node: DeepReadonly<TSESTree.Node> | undefined | null,
|
|
294
301
|
): boolean => {
|
|
@@ -125,6 +125,64 @@ describe('check-destructuring-completeness', () => {
|
|
|
125
125
|
};
|
|
126
126
|
`,
|
|
127
127
|
},
|
|
128
|
+
{
|
|
129
|
+
name: 'handles string literal keys with directive - complete',
|
|
130
|
+
code: dedent`
|
|
131
|
+
const obj: { a: number; 'data-e2eId': string } = { a: 1, 'data-e2eId': 'test' };
|
|
132
|
+
// @check-destructuring-completeness
|
|
133
|
+
const { a, 'data-e2eId': e2eId } = obj;
|
|
134
|
+
`,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'handles string literal keys in React component props - parameter destructuring',
|
|
138
|
+
code: dedent`
|
|
139
|
+
type Props = { a: number; b: string; 'data-e2eId': string };
|
|
140
|
+
const MyComponent = ({ a, b, 'data-e2eId': e2eId }: Props) => <div>{a}{b}{e2eId}</div>;
|
|
141
|
+
`,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'handles string literal keys in React component props - internal destructuring',
|
|
145
|
+
code: dedent`
|
|
146
|
+
type Props = { a: number; b: string; 'data-e2eId': string };
|
|
147
|
+
const MyComponent = (props: Props) => {
|
|
148
|
+
const { a, b, 'data-e2eId': e2eId } = props;
|
|
149
|
+
return <div>{a}{b}{e2eId}</div>;
|
|
150
|
+
};
|
|
151
|
+
`,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'handles number literal keys with directive - complete',
|
|
155
|
+
code: dedent`
|
|
156
|
+
const obj: { 0: string; 1: number } = { 0: 'a', 1: 2 };
|
|
157
|
+
// @check-destructuring-completeness
|
|
158
|
+
const { 0: first, 1: second } = obj;
|
|
159
|
+
`,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'skips check when dynamic computed key is present with directive',
|
|
163
|
+
code: dedent`
|
|
164
|
+
const key = 'a' as string;
|
|
165
|
+
const obj: { a: number; b: string } = { a: 1, b: 'hello' };
|
|
166
|
+
// @check-destructuring-completeness
|
|
167
|
+
const { [key]: val } = obj;
|
|
168
|
+
`,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'skips check when dynamic computed key is present in React component props',
|
|
172
|
+
code: dedent`
|
|
173
|
+
const key = 'a' as string;
|
|
174
|
+
type Props = { a: number; b: string };
|
|
175
|
+
const MyComponent = ({ [key]: val }: Props) => <div>{val}</div>;
|
|
176
|
+
`,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'handles computed key with static string literal',
|
|
180
|
+
code: dedent`
|
|
181
|
+
const obj: { a: number; 'data-id': string } = { a: 1, 'data-id': 'test' };
|
|
182
|
+
// @check-destructuring-completeness
|
|
183
|
+
const { a, ['data-id']: dataId } = obj;
|
|
184
|
+
`,
|
|
185
|
+
},
|
|
128
186
|
{
|
|
129
187
|
name: 'does not check React component props when disabled - parameter',
|
|
130
188
|
code: dedent`
|
|
@@ -165,6 +223,43 @@ describe('check-destructuring-completeness', () => {
|
|
|
165
223
|
options: [{ directiveKeyword: '@custom-check' }],
|
|
166
224
|
errors: [{ messageId: 'incompleteDestructuring' }],
|
|
167
225
|
},
|
|
226
|
+
{
|
|
227
|
+
name: 'reports missing string literal key with directive',
|
|
228
|
+
code: dedent`
|
|
229
|
+
const obj: { a: number; 'data-e2eId': string } = { a: 1, 'data-e2eId': 'test' };
|
|
230
|
+
// @check-destructuring-completeness
|
|
231
|
+
const { a } = obj;
|
|
232
|
+
`,
|
|
233
|
+
errors: [{ messageId: 'incompleteDestructuring' }],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: 'reports missing string literal key in React component props - parameter',
|
|
237
|
+
code: dedent`
|
|
238
|
+
type Props = { a: number; 'data-e2eId': string };
|
|
239
|
+
const MyComponent = ({ a }: Props) => <div>{a}</div>;
|
|
240
|
+
`,
|
|
241
|
+
errors: [{ messageId: 'incompleteDestructuring' }],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'reports missing string literal key in React component props - internal',
|
|
245
|
+
code: dedent`
|
|
246
|
+
type Props = { a: number; 'data-e2eId': string };
|
|
247
|
+
const MyComponent = (props: Props) => {
|
|
248
|
+
const { a } = props;
|
|
249
|
+
return <div>{a}</div>;
|
|
250
|
+
};
|
|
251
|
+
`,
|
|
252
|
+
errors: [{ messageId: 'incompleteDestructuring' }],
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'reports missing number literal key with directive',
|
|
256
|
+
code: dedent`
|
|
257
|
+
const obj: { 0: string; 1: number } = { 0: 'a', 1: 2 };
|
|
258
|
+
// @check-destructuring-completeness
|
|
259
|
+
const { 0: first } = obj;
|
|
260
|
+
`,
|
|
261
|
+
errors: [{ messageId: 'incompleteDestructuring' }],
|
|
262
|
+
},
|
|
168
263
|
{
|
|
169
264
|
name: 'checks React component props - parameter destructuring incomplete',
|
|
170
265
|
code: dedent`
|