Skip to content

Commit 2a956b2

Browse files
authored
fix(eslint-plugin): [no-deprecated] report on deprecated properties with function-like types (typescript-eslint#9977)
1 parent 9a80067 commit 2a956b2

File tree

3 files changed

+179
-1
lines changed

3 files changed

+179
-1
lines changed

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,32 @@ export default createRule({
172172
const signature = checker.getResolvedSignature(
173173
tsNode as ts.CallLikeExpression,
174174
);
175+
const symbol = services.getSymbolAtLocation(node);
175176
if (signature) {
176177
const signatureDeprecation = getJsDocDeprecation(signature);
177178
if (signatureDeprecation !== undefined) {
178179
return signatureDeprecation;
179180
}
181+
182+
// Properties with function-like types have "deprecated" jsdoc
183+
// on their symbols, not on their signatures:
184+
//
185+
// interface Props {
186+
// /** @deprecated */
187+
// property: () => 'foo'
188+
// ^symbol^ ^signature^
189+
// }
190+
const symbolDeclarationKind = symbol?.declarations?.[0].kind;
191+
if (
192+
symbolDeclarationKind !== ts.SyntaxKind.MethodDeclaration &&
193+
symbolDeclarationKind !== ts.SyntaxKind.FunctionDeclaration &&
194+
symbolDeclarationKind !== ts.SyntaxKind.MethodSignature
195+
) {
196+
return getJsDocDeprecation(symbol);
197+
}
180198
}
181199

182200
// Or it could be a ClassDeclaration or a variable set to a ClassExpression.
183-
const symbol = services.getSymbolAtLocation(node);
184201
const symbolAtLocation =
185202
symbol && checker.getTypeOfSymbolAtLocation(symbol, tsNode).getSymbol();
186203

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

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,32 @@ ruleTester.run('no-deprecated', rule, {
100100
101101
a('b');
102102
`,
103+
`
104+
class A {
105+
a(value: 'b'): void;
106+
/** @deprecated */
107+
a(value: 'c'): void;
108+
}
109+
declare const foo: A;
110+
foo.a('b');
111+
`,
112+
`
113+
type A = {
114+
(value: 'b'): void;
115+
/** @deprecated */
116+
(value: 'c'): void;
117+
};
118+
declare const foo: A;
119+
foo('b');
120+
`,
121+
`
122+
declare const a: {
123+
new (value: 'b'): void;
124+
/** @deprecated */
125+
new (value: 'c'): void;
126+
};
127+
new a('b');
128+
`,
103129
`
104130
namespace assert {
105131
export function fail(message?: string | Error): never;
@@ -651,6 +677,26 @@ ruleTester.run('no-deprecated', rule, {
651677
},
652678
],
653679
},
680+
{
681+
code: `
682+
declare const A: {
683+
/** @deprecated */
684+
new (): string;
685+
};
686+
687+
new A();
688+
`,
689+
errors: [
690+
{
691+
column: 13,
692+
endColumn: 14,
693+
line: 7,
694+
endLine: 7,
695+
data: { name: 'A' },
696+
messageId: 'deprecated',
697+
},
698+
],
699+
},
654700
{
655701
code: `
656702
/** @deprecated */
@@ -737,6 +783,99 @@ ruleTester.run('no-deprecated', rule, {
737783
},
738784
],
739785
},
786+
{
787+
code: `
788+
declare class A {
789+
/** @deprecated */
790+
b: () => string;
791+
}
792+
793+
declare const a: A;
794+
795+
a.b;
796+
`,
797+
errors: [
798+
{
799+
column: 11,
800+
endColumn: 12,
801+
line: 9,
802+
endLine: 9,
803+
data: { name: 'b' },
804+
messageId: 'deprecated',
805+
},
806+
],
807+
},
808+
{
809+
code: `
810+
declare class A {
811+
/** @deprecated */
812+
b: () => string;
813+
}
814+
815+
declare const a: A;
816+
817+
a.b();
818+
`,
819+
only: false,
820+
errors: [
821+
{
822+
column: 11,
823+
endColumn: 12,
824+
line: 9,
825+
endLine: 9,
826+
data: { name: 'b' },
827+
messageId: 'deprecated',
828+
},
829+
],
830+
},
831+
{
832+
code: `
833+
interface A {
834+
/** @deprecated */
835+
b: () => string;
836+
}
837+
838+
declare const a: A;
839+
840+
a.b();
841+
`,
842+
only: false,
843+
errors: [
844+
{
845+
column: 11,
846+
endColumn: 12,
847+
line: 9,
848+
endLine: 9,
849+
data: { name: 'b' },
850+
messageId: 'deprecated',
851+
},
852+
],
853+
},
854+
{
855+
code: `
856+
class A {
857+
/** @deprecated */
858+
b(): string {
859+
return '';
860+
}
861+
}
862+
863+
declare const a: A;
864+
865+
a.b();
866+
`,
867+
only: false,
868+
errors: [
869+
{
870+
column: 11,
871+
endColumn: 12,
872+
line: 11,
873+
endLine: 11,
874+
data: { name: 'b' },
875+
messageId: 'deprecated',
876+
},
877+
],
878+
},
740879
{
741880
code: `
742881
declare class A {
@@ -1155,6 +1294,27 @@ ruleTester.run('no-deprecated', rule, {
11551294
},
11561295
],
11571296
},
1297+
{
1298+
code: `
1299+
type A = {
1300+
(value: 'b'): void;
1301+
/** @deprecated */
1302+
(value: 'c'): void;
1303+
};
1304+
declare const foo: A;
1305+
foo('c');
1306+
`,
1307+
errors: [
1308+
{
1309+
column: 9,
1310+
endColumn: 12,
1311+
line: 8,
1312+
endLine: 8,
1313+
data: { name: 'foo' },
1314+
messageId: 'deprecated',
1315+
},
1316+
],
1317+
},
11581318
{
11591319
code: `
11601320
function a(

packages/utils/tests/ts-eslint/ESLint.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('ESLint', () => {
1313
expect(eslint).toBeInstanceOf(FlatESLint);
1414
});
1515
it('legacy', () => {
16+
// eslint-disable-next-line @typescript-eslint/no-deprecated
1617
const eslint = new ESLint.LegacyESLint({
1718
// accepts legacy configs
1819
baseConfig: {

0 commit comments

Comments
 (0)
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