8000 fix(eslint-plugin): [no-deprecated] support computed member access (#… · coreyward/typescript-eslint@523b3ea · GitHub
[go: up one dir, main page]

Skip to content

Commit 523b3ea

Browse files
fix(eslint-plugin): [no-deprecated] support computed member access (typescript-eslint#10867)
* fix(eslint-plugin): [no-deprecated] support computed member access * test(eslint-plugin): [no-deprecated] increase test coverage * fix(eslint-plugin): [no-deprecated] add fix by review --------- Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>
1 parent 0d822bd commit 523b3ea

File tree

2 files changed

+306
-4
lines changed

2 files changed

+306
-4
lines changed

packages/eslint-plugin/src/rules/no-deprecated.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,13 +395,54 @@ export default createRule<Options, MessageIds>({
395395
});
396396
}
397397

398+
function checkMemberExpression(node: TSESTree.MemberExpression): void {
399+
if (!node.computed) {
400+
return;
401+
}
402+
403+
const propertyType = services.getTypeAtLocation(node.property);
404+
405+
if (propertyType.isLiteral()) {
406+
const objectType = services.getTypeAtLocation(node.object);
407+
408+
const propertyName = propertyType.isStringLiteral()
409+
? propertyType.value
410+
: String(propertyType.value as number);
411+
412+
const property = objectType.getProperty(propertyName);
413+
414+
const reason = getJsDocDeprecation(property);
415+
if (reason == null) {
416+
return;
417+
}
418+
419+
if (typeMatchesSomeSpecifier(objectType, allow, services.program)) {
420+
return;
421+
}
422+
423+
context.report({
424+
...(reason
425+
? {
426+
messageId: 'deprecatedWithReason',
427+
data: { name: propertyName, reason },
428+
}
429+
: {
430+
messageId: 'deprecated',
431+
data: { name: propertyName },
432+
}),
433+
node: node.property,
434+
});
435+
}
436+
}
437+
398438
return {
399439
Identifier: checkIdentifier,
400440
JSXIdentifier(node): void {
401441
if (node.parent.type !== AST_NODE_TYPES.JSXClosingElement) {
402442
checkIdentifier(node);
403443
}
404444
},
445+
MemberExpression: checkMemberExpression,
405446
PrivateIdentifier: checkIdentifier,
406447
Super: checkIdentifier,
407448
};

packages/eslint-plugin/tests/rules/no-deprecated.test.ts

Lines changed: 265 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,13 +364,126 @@ exists('/foo');
364364
const bar = { test };
365365
`,
366366
`
367-
class A {
368-
#b = () => {};
367+
const a = {
368+
/** @deprecated */
369+
b: 'string',
370+
};
369371
370-
c() {
371-
this.#b();
372+
const complex = Symbol() as any;
373+
const c = a[complex];
374+
`,
375+
`
376+
const a = {
377+
b: 'string',
378+
};
379+
380+
const c = a['b'];
381+
`,
382+
{
383+
code: `
384+
interface AllowedType {
385+
/** @deprecated */
386+
prop: string;
372387
}
388+
389+
const obj: AllowedType = {
390+
prop: 'test',
391+
};
392+
393+
const value = obj['prop'];
394+
`,
395+
options: [
396+
{
397+
allow: [
398+
{
399+
from: 'file',
400+
name: 'AllowedType',
401+
},
402+
],
403+
},
404+
],
405+
},
406+
`
407+
const a = {
408+
/** @deprecated */
409+
b: 'string',
410+
};
411+
412+
const key = {};
413+
const c = a[key as any];
414+
`,
415+
`
416+
const a = {
417+
/** @deprecated */
418+
b: 'string',
419+
};
420+
421+
const key = Symbol();
422+
const c = a[key as any];
423+
`,
424+
`
425+
const a = {
426+
/** @deprecated */
427+
b: 'string',
428+
};
429+
430+
const key = undefined;
431+
const c = a[key as any];
432+
`,
433+
`
434+
const a = {
435+
/** @deprecated */
436+
b: 'string',
437+
};
438+
439+
const c = a['nonExistentProperty'];
440+
`,
441+
`
442+
const a = {
443+
/** @deprecated */
444+
b: 'string',
445+
};
446+
447+
function getKey() {
448+
return 'c';
373449
}
450+
451+
const c = a[getKey()];
452+
`,
453+
`
454+
const a = {
455+
/** @deprecated */
456+
b: 'string',
457+
};
458+
459+
const key = {};
460+
const c = a[key];
461+
`,
462+
`
463+
const stringObj = new String('b');
464+
const a = {
465+
/** @deprecated */
466+
b: 'string',
467+
};
468+
const c = a[stringObj];
469+
`,
470+
`
471+
const a = {
472+
/** @deprecated */
473+
b: 'string',
474+
};
475+
476+
const key = Symbol('key');
477+
const c = a[key];
478+
`,
479+
`
480+
const a = {
481+
/** @deprecated */
482+
b: 'string',
483+
};
484+
485+
const key = null;
486+
const c = a[key as any];
374487
`,
375488
],
376489
invalid: [
@@ -2912,5 +3025,153 @@ class B extends A {
29123025
},
29133026
],
29143027
},
3028+
{
3029+
code: `
3030+
const a = {
3031+
/** @deprecated */
3032+
b: 'string',
3033+
};
3034+
3035+
const c = a['b'];
3036+
`,
3037+
errors: [
3038+
{
3039+
column: 21,
3040+
data: { name: 'b' },
3041+
endColumn: 24,
3042+
endLine: 7,
3043+
line: 7,
3044+
messageId: 'deprecated',
3045+
},
3046+
],
3047+
},
3048+
{
3049+
code: `
3050+
const a = {
3051+
/** @deprecated */
3052+
b: 'string',
3053+
};
3054+
const x = 'b';
3055+
const c = a[x];
3056+
`,
3057+
errors: [
3058+
{
3059+
column: 21,
3060+
data: { name: 'b' },
3061+
endColumn: 22,
3062+
endLine: 7,
3063+
line: 7,
3064+
messageId: 'deprecated',
3065+
},
3066+
],
3067+
},
3068+
{
3069+
code: `
3070+
const a = {
3071+
/** @deprecated */
3072+
[2]: 'string',
3073+
};
3074+
const x = 'b';
3075+
const c = a[2];
3076+
`,
3077+
errors: [
3078+
{
3079+
column: 21,
3080+
data: { name: '2' },
3081+
endColumn: 22,
3082+
endLine: 7,
3083+
line: 7,
3084+
messageId: 'deprecated',
3085+
},
3086+
],
3087+
},
3088+
{
3089+
code: `
3090+
const a = {
3091+
/** @deprecated reason for deprecation */
3092+
b: 'string',
3093+
};
3094+
3095+
const key = 'b';
3096+
const stringKey = key as const;
3097+
const c = a[stringKey];
3098+
`,
3099+
errors: [
3100+
{
3101+
column: 21,
3102+
data: { name: 'b', reason: 'reason for deprecation' },
3103+
endColumn: 30,
3104+
endLine: 9,
3105+
line: 9,
3106+
messageId: 'deprecatedWithReason',
3107+
},
3108+
],
3109+
},
3110+
{
3111+
code: `
3112+
enum Keys {
3113+
B = 'b',
3114+
}
3115+
3116+
const a = {
3117+
/** @deprecated reason for deprecation */
3118+
b: 'string',
3119+
};
3120+
3121+
const key = Keys.B;
3122+
const c = a[key];
3123+
`,
3124+
errors: [
3125+
{
3126+
column: 21,
3127+
data: { name: 'b', reason: 'reason for deprecation' },
3128+
endColumn: 24,
3129+
endLine: 12,
3130+
line: 12,
3131+
messageId: 'deprecatedWithReason',
3132+
},
3133+
],
3134+
},
3135+
{
3136+
code: `
3137+
const a = {
3138+
/** @deprecated */
3139+
b: 'string',
3140+
};
3141+
3142+
const key = \`b\`;
3143+
const c = a[key];
3144+
`,
3145+
errors: [
3146+
{
3147+
column: 21,
3148+
data: { name: 'b' },
3149+
endColumn: 24,
3150+
endLine: 8,
3151+
line: 8,
3152+
messageId: 'deprecated',
3153+
},
3154+
],
3155+
},
3156+
{
3157+
code: `
3158+
const stringObj = 'b';
3159+
const a = {
3160+
/** @deprecated */
3161+
b: 'string',
3162+
};
3163+
const c = a[stringObj];
3164+
`,
3165+
errors: [
3166+
{
3167+
column: 21,
3168+
data: { name: 'b' },
3169+
endColumn: 30,
3170+
endLine: 7,
3171+
line: 7,
3172+
messageId: 'deprecated',
3173+
},
3174+
],
3175+
},
29153176
],
29163177
});

0 commit comments

Comments
 (0)
0