Skip to content

Commit 28ae396

Browse files
authored
Merge pull request #19741 from Napalys/js/quality/suspicious_method_names
JS: Promote `js/suspicious-method-name-declaration` to the Code Quality suite.
2 parents e1b4dea + e6d2691 commit 28ae396

12 files changed

+159
-26
lines changed

javascript/ql/integration-tests/query-suite/javascript-code-quality.qls.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
ql/javascript/ql/src/Declarations/IneffectiveParameterType.ql
2+
ql/javascript/ql/src/Declarations/SuspiciousMethodNameDeclaration.ql
23
ql/javascript/ql/src/Expressions/ExprHasNoEffect.ql
34
ql/javascript/ql/src/Expressions/MissingAwait.ql
45
ql/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql

javascript/ql/src/Declarations/SuspiciousMethodNameDeclaration.qhelp

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,73 @@
44
<qhelp>
55
<overview>
66
<p>
7-
In TypeScript the keywords <code>constructor</code> and <code>new</code> for
8-
member declarations are used to declare constructors in classes and interfaces
9-
respectively.
10-
However, a member declaration with the name <code>new</code> in an interface
11-
or <code>constructor</code> in a class, will declare an ordinary method named
12-
<code>new</code> or <code>constructor</code> rather than a constructor.
13-
Similarly, the keyword <code>function</code> is used to declare functions in
14-
some contexts. However, using the name <code>function</code> for a class
15-
or interface member declaration declares a method named <code>function</code>.
7+
In TypeScript, certain keywords have special meanings for member declarations, and misusing them can create confusion:
8+
</p>
9+
10+
<ul>
11+
<li>In classes, use <code>constructor</code> rather than <code>new</code> to declare constructors. Using <code>new</code> within a class creates a method named "new" and not a constructor signature.</li>
12+
<li>In interfaces, use <code>new</code> rather than <code>constructor</code> to declare constructor signatures. Using <code>constructor</code> within an interface creates a method named "constructor" and not a constructor signature.</li>
13+
<li>Similarly, the keyword <code>function</code> is used to declare functions in some contexts. However, using the name <code>function</code> for a class or interface member declaration declares a method named "function".</li>
14+
</ul>
15+
16+
<p>
17+
When these keywords are misused, TypeScript will interpret them as regular method names rather than their intended special syntax, leading to code that may not work as expected.
1618
</p>
1719

1820
</overview>
1921
<recommendation>
2022

2123
<p>
22-
Declare classes as classes and not as interfaces.
23-
Use the keyword <code>constructor</code> to declare constructors in a class,
24-
use the keyword <code>new</code> to declare constructors inside interfaces,
25-
and don't use <code>function</code> when declaring a call signature in an
26-
interface.
24+
Consider following these guidelines for clearer code:
2725
</p>
2826

27+
<ul>
28+
<li>For classes, use <code>constructor</code> to declare constructors.</li>
29+
<li>For interfaces, use <code>new</code> to declare constructor signatures (call signatures that create new instances).</li>
30+
<li>Avoid accidentally creating methods named <code>function</code> by misusing the <code>function</code> keyword within class or interface declarations.</li>
31+
</ul>
32+
2933
</recommendation>
3034
<example>
3135

3236
<p>
33-
The below example declares an interface <code>Point</code> with 2 fields
34-
and a method called <code>constructor</code>. The interface does not declare
35-
a class <code>Point</code> with a constructor, which was likely what the
36-
developer meant to create.
37+
The following examples show common mistakes when using these keywords:
3738
</p>
38-
<sample src="examples/SuspiciousMethodNameDeclaration.ts" />
3939

4040
<p>
41-
The below example is a fixed version of the above, where the interface is
42-
instead declared as a class, thereby describing the type the developer meant
43-
in the first place.
41+
This interface mistakenly uses <code>constructor</code>, which creates a method named "constructor" instead of a constructor signature:
4442
</p>
43+
<sample src="examples/SuspiciousMethodNameDeclaration.ts" />
4544

45+
<p>
46+
Use <code>new</code> for constructor signatures in interfaces:
47+
</p>
4648
<sample src="examples/SuspiciousMethodNameDeclarationFixed.ts" />
4749

50+
<p>
51+
This class mistakenly uses <code>new</code>, which creates a method named "new" instead of a constructor:
52+
</p>
53+
<sample src="examples/SuspiciousMethodNameDeclarationClass.ts" />
54+
55+
<p>
56+
Use <code>constructor</code> for constructors in classes:
57+
</p>
58+
<sample src="examples/SuspiciousMethodNameDeclarationClassFixed.ts" />
59+
60+
<p>
61+
This interface uses <code>function</code> as a method name, which declares a method named "function" rather than declaring a function:
62+
</p>
63+
<sample src="examples/SuspiciousMethodNameDeclarationFunction.ts" />
64+
65+
<p>
66+
Use a descriptive method name instead:
67+
</p>
68+
<sample src="examples/SuspiciousMethodNameDeclarationFunctionFixed.ts" />
69+
4870
</example>
4971
<references>
5072

73+
<li>TypeScript Handbook: <a href="https://www.typescriptlang.org/docs/handbook/2/classes.html#constructors">Classes - Constructors</a>.</li>
5174
<li>TypeScript specification: <a href="https://github.com/microsoft/TypeScript/blob/30cb20434a6b117e007a4959b2a7c16489f86069/doc/spec-ARCHIVED.md#3.8.9">Constructor Type Literals</a>.</li>
5275
<li>TypeScript specification: <a href="https://github.com/microsoft/TypeScript/blob/30cb20434a6b117e007a4959b2a7c16489f86069/doc/spec-ARCHIVED.md#8.3.1">Constructor Parameters</a>.</li>
5376

javascript/ql/src/Declarations/SuspiciousMethodNameDeclaration.ql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
* @problem.severity warning
77
* @id js/suspicious-method-name-declaration
88
* @precision high
9-
* @tags correctness
9+
* @tags quality
10+
* reliability
11+
* correctness
1012
* typescript
1113
* methods
1214
*/
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
declare class Point {
1+
// BAD: Using 'constructor' in an interface creates a method, not a constructor signature
2+
interface Point {
23
x: number;
34
y: number;
4-
constructor(x : number, y: number);
5+
constructor(x: number, y: number); // This is just a method named "constructor"
56
}
6-
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// BAD: Using 'new' in a class creates a method, not a constructor
2+
class Point {
3+
x: number;
4+
y: number;
5+
new(x: number, y: number) {}; // This is just a method named "new"
6+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// GOOD: Using 'constructor' for constructors in classes
2+
class Point {
3+
x: number;
4+
y: number;
5+
constructor(x: number, y: number) { // This is a proper constructor
6+
this.x = x;
7+
this.y = y;
8+
}
9+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
// GOOD: Using 'new' for constructor signatures in interfaces
12
interface Point {
23
x: number;
34
y: number;
5+
new(x: number, y: number): Point; // This is a proper constructor signature
46
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// BAD: Using 'function' as a method name is confusing
2+
interface Calculator {
3+
function(a: number, b: number): number; // This is just a method named "function"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// GOOD: Using descriptive method names instead of 'function'
2+
interface Calculator {
3+
calculate(a: number, b: number): number; // Clear, descriptive method name
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: queryMetadata
3+
---
4+
* Added `reliability` tag to the `js/suspicious-method-name-declaration` query.

javascript/ql/test/query-tests/Declarations/SuspiciousMethodNameDeclaration/SuspiciousMethodNameDeclaration.expected

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,14 @@
22
| tst.ts:16:3:16:21 | function(): number; | The member name 'function' does not declare a function, it declares a method named 'function'. |
33
| tst.ts:37:3:37:21 | function(): number; | The member name 'function' does not declare a function, it declares a method named 'function'. |
44
| tst.ts:48:3:48:13 | new(): Quz; | The member name 'new' does not declare a constructor, but 'constructor' does in class declarations. |
5+
| tst.ts:60:3:60:21 | function(): number; | The member name 'function' does not declare a function, it declares a method named 'function'. |
6+
| tst.ts:64:3:64:24 | constru ... number; | The member name 'constructor' does not declare a constructor in interfaces, but it does in classes. |
7+
| tst.ts:74:3:74:30 | functio ... string; | The member name 'function' does not declare a function, it declares a method named 'function'. |
8+
| tst.ts:75:3:75:30 | functio ... number; | The member name 'function' does not declare a function, it declares a method named 'function'. |
9+
| tst.ts:76:3:76:24 | functio ... ): any; | The member name 'function' does not declare a function, it declares a method named 'function'. |
10+
| tst.ts:80:3:80:23 | abstrac ... : void; | The member name 'new' does not declare a constructor, but 'constructor' does in class declarations. |
11+
| tst.ts:84:3:84:30 | abstrac ... number; | The member name 'function' does not declare a function, it declares a method named 'function'. |
12+
| tst.ts:93:5:93:21 | function(): void; | The member name 'function' does not declare a function, it declares a method named 'function'. |
13+
| tst.ts:98:3:98:21 | function(): number; | The member name 'function' does not declare a function, it declares a method named 'function'. |
14+
| tst.ts:110:3:110:24 | constru ... number; | The member name 'constructor' does not declare a constructor in interfaces, but it does in classes. |
15+
| tst.ts:116:3:116:24 | constru ... number; | The member name 'constructor' does not declare a constructor in interfaces, but it does in classes. |

javascript/ql/test/query-tests/Declarations/SuspiciousMethodNameDeclaration/tst.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,70 @@ declare class Quz {
5050

5151
var bla = new Foo();
5252
var blab = new Baz();
53+
54+
55+
interface X {
56+
constructor: () => string; // Just a property, not a method.
57+
}
58+
59+
type A = {
60+
function(): number; // $ Alert
61+
};
62+
63+
type B = {
64+
constructor(): number; // $ Alert
65+
new(): number;
66+
};
67+
68+
class StaticMethods {
69+
static function(): void {}
70+
static new(): void {}
71+
}
72+
73+
interface Overloaded {
74+
function(x: string): string; // $Alert
75+
function(x: number): number; // $Alert
76+
function(x: any): any; // $Alert
77+
}
78+
79+
abstract class AbstractFoo {
80+
abstract new(): void; // $Alert
81+
}
82+
83+
abstract class AbstractFooFunction {
84+
abstract function(): number; // $Alert
85+
}
86+
87+
abstract class AbstractFooConstructor {
88+
constructor(){}
89+
}
90+
91+
declare module "some-module" {
92+
interface ModuleInterface {
93+
function(): void; // $Alert
94+
}
95+
}
96+
97+
type Intersection = {
98+
function(): number; // $Alert
99+
} & {
100+
other(): string;
101+
};
102+
103+
type Union = {
104+
new(): number;
105+
} | {
106+
valid(): string;
107+
};
108+
109+
type Union2 = {
110+
constructor(): number; // $Alert
111+
} | {
112+
valid(): string;
113+
};
114+
115+
type Intersection2 = {
116+
constructor(): number; // $Alert
117+
} & {
118+
other(): string;
119+
};

0 commit comments

Comments
 (0)