8000 fix(eslint-plugin): [no-deprecated] report on deprecated properties w… · ronami/typescript-eslint@2a956b2 · GitHub
[go: up one dir, main page]

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