From ca600d7851449a2351c1826f96578f7219e987b1 Mon Sep 17 00:00:00 2001 From: "Azat S." Date: Sat, 22 Feb 2025 14:44:03 +0300 Subject: [PATCH 1/3] fix(eslint-plugin): [no-deprecated] support computed member access --- .../eslint-plugin/src/rules/no-deprecated.ts | 51 ++++++++++++++++ .../tests/rules/no-deprecated.test.ts | 60 +++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index fb426747edd2..ebb9b4fc2f7a 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -395,6 +395,56 @@ export default createRule({ }); } + function checkMemberExpression(node: TSESTree.MemberExpression): void { + if (!node.computed) { + return; + } + + const propertyType = services.getTypeAtLocation(node.property); + + if (propertyType.isStringLiteral() || propertyType.isLiteral()) { + const objectType = services.getTypeAtLocation(node.object); + + let propertyName: string | undefined; + + if (propertyType.isStringLiteral()) { + propertyName = propertyType.value; + } else if (typeof propertyType.value === 'string') { + propertyName = propertyType.value; + } else if (typeof propertyType.value === 'number') { + propertyName = String(propertyType.value); + } + + if (!propertyName) { + return; + } + + const property = objectType.getProperty(propertyName); + + const reason = getJsDocDeprecation(property); + if (reason == null) { + return; + } + + if (typeMatchesSomeSpecifier(objectType, allow, services.program)) { + return; + } + + context.report({ + ...(reason + ? { + messageId: 'deprecatedWithReason', + data: { name: propertyName, reason }, + } + : { + messageId: 'deprecated', + data: { name: propertyName }, + }), + node: node.property, + }); + } + } + return { Identifier: checkIdentifier, JSXIdentifier(node): void { @@ -402,6 +452,7 @@ export default createRule({ checkIdentifier(node); } }, + MemberExpression: checkMemberExpression, PrivateIdentifier: checkIdentifier, Super: checkIdentifier, }; diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index 43ea34aa5471..b342bdb6bcd1 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -2912,5 +2912,65 @@ class B extends A { }, ], }, + { + code: ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const c = a['b']; + `, + errors: [ + { + column: 21, + data: { name: 'b' }, + endColumn: 24, + endLine: 7, + line: 7, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + const a = { + /** @deprecated */ + b: 'string', + }; + const x = 'b'; + const c = a[x]; + `, + errors: [ + { + column: 21, + data: { name: 'b' }, + endColumn: 22, + endLine: 7, + line: 7, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + const a = { + /** @deprecated */ + [2]: 'string', + }; + const x = 'b'; + const c = a[2]; + `, + errors: [ + { + column: 21, + data: { name: '2' }, + endColumn: 22, + endLine: 7, + line: 7, + messageId: 'deprecated', + }, + ], + }, ], }); From a19754f6703dcc41444ddd29da732b87d7fb6c15 Mon Sep 17 00:00:00 2001 From: "Azat S." Date: Sun, 2 Mar 2025 14:59:26 +0300 Subject: [PATCH 2/3] test(eslint-plugin): [no-deprecated] increase test coverage --- .../eslint-plugin/src/rules/no-deprecated.ts | 12 +- .../tests/rules/no-deprecated.test.ts | 209 +++++++++++++++++- 2 files changed, 208 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index ebb9b4fc2f7a..5bdcbd85a97f 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -405,18 +405,12 @@ export default createRule({ if (propertyType.isStringLiteral() || propertyType.isLiteral()) { const objectType = services.getTypeAtLocation(node.object); - let propertyName: string | undefined; + let propertyName: string; if (propertyType.isStringLiteral()) { propertyName = propertyType.value; - } else if (typeof propertyType.value === 'string') { - propertyName = propertyType.value; - } else if (typeof propertyType.value === 'number') { - propertyName = String(propertyType.value); - } - - if (!propertyName) { - return; + } else { + propertyName = String(propertyType.value as number); } const property = objectType.getProperty(propertyName); diff --git a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts index b342bdb6bcd1..1c8f354886df 100644 --- a/packages/eslint-plugin/tests/rules/no-deprecated.test.ts +++ b/packages/eslint-plugin/tests/rules/no-deprecated.test.ts @@ -364,13 +364,126 @@ exists('/foo'); const bar = { test }; `, ` - class A { - #b = () => {}; + const a = { + /** @deprecated */ + b: 'string', + }; + + const complex = Symbol() as any; + const c = a[complex]; + `, + ` + const a = { + b: 'string', + }; - c() { - this.#b(); + const c = a['b']; + `, + { + code: ` + interface AllowedType { + /** @deprecated */ + prop: string; } + + const obj: AllowedType = { + prop: 'test', + }; + + const value = obj['prop']; + `, + options: [ + { + allow: [ + { + from: 'file', + name: 'AllowedType', + }, + ], + }, + ], + }, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const key = {}; + const c = a[key as any]; + `, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const key = Symbol(); + const c = a[key as any]; + `, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const key = undefined; + const c = a[key as any]; + `, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const c = a['nonExistentProperty']; + `, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + function getKey() { + return 'c'; } + + const c = a[getKey()]; + `, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const key = {}; + const c = a[key]; + `, + ` + const stringObj = new String('b'); + const a = { + /** @deprecated */ + b: 'string', + }; + const c = a[stringObj]; + `, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const key = Symbol('key'); + const c = a[key]; + `, + ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const key = null; + const c = a[key as any]; `, ], invalid: [ @@ -2972,5 +3085,93 @@ class B extends A { }, ], }, + { + code: ` + const a = { + /** @deprecated reason for deprecation */ + b: 'string', + }; + + const key = 'b'; + const stringKey = key as const; + const c = a[stringKey]; + `, + errors: [ + { + column: 21, + data: { name: 'b', reason: 'reason for deprecation' }, + endColumn: 30, + endLine: 9, + line: 9, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + enum Keys { + B = 'b', + } + + const a = { + /** @deprecated reason for deprecation */ + b: 'string', + }; + + const key = Keys.B; + const c = a[key]; + `, + errors: [ + { + column: 21, + data: { name: 'b', reason: 'reason for deprecation' }, + endColumn: 24, + endLine: 12, + line: 12, + messageId: 'deprecatedWithReason', + }, + ], + }, + { + code: ` + const a = { + /** @deprecated */ + b: 'string', + }; + + const key = \`b\`; + const c = a[key]; + `, + errors: [ + { + column: 21, + data: { name: 'b' }, + endColumn: 24, + endLine: 8, + line: 8, + messageId: 'deprecated', + }, + ], + }, + { + code: ` + const stringObj = 'b'; + const a = { + /** @deprecated */ + b: 'string', + }; + const c = a[stringObj]; + `, + errors: [ + { + column: 21, + data: { name: 'b' }, + endColumn: 30, + endLine: 7, + line: 7, + messageId: 'deprecated', + }, + ], + }, ], }); From 8bff4a61aff9f78ae54b4f570e38063790918c60 Mon Sep 17 00:00:00 2001 From: "Azat S." Date: Mon, 5 May 2025 19:20:28 +0300 Subject: [PATCH 3/3] fix(eslint-plugin): [no-deprecated] add fix by review --- packages/eslint-plugin/src/rules/no-deprecated.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-deprecated.ts b/packages/eslint-plugin/src/rules/no-deprecated.ts index 5bdcbd85a97f..725ddaf3fce9 100644 --- a/packages/eslint-plugin/src/rules/no-deprecated.ts +++ b/packages/eslint-plugin/src/rules/no-deprecated.ts @@ -402,16 +402,12 @@ export default createRule({ const propertyType = services.getTypeAtLocation(node.property); - if (propertyType.isStringLiteral() || propertyType.isLiteral()) { + if (propertyType.isLiteral()) { const objectType = services.getTypeAtLocation(node.object); - let propertyName: string; - - if (propertyType.isStringLiteral()) { - propertyName = propertyType.value; - } else { - propertyName = String(propertyType.value as number); - } + const propertyName = propertyType.isStringLiteral() + ? propertyType.value + : String(propertyType.value as number); const property = objectType.getProperty(propertyName); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy