Skip to content

Commit 557a0d2

Browse files
feat: support TypeScript syntax in no-useless-constructor (#19535)
* feat: support TypeScript syntax in no-useless-constructor * Remove unnecessary // ... * re-ran trunk fmt * git checkout main -- docs/src/_data/further_reading_links.json * touch up node.value.body check * ...as well as test newline * Add meta.dialects, meta.language
1 parent 2357edd commit 557a0d2

File tree

3 files changed

+241
-1
lines changed

3 files changed

+241
-1
lines changed

docs/src/rules/no-useless-constructor.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,44 @@ class D extends A {
7777

7878
:::
7979

80+
This rule additionally supports TypeScript type syntax.
81+
82+
Examples of **incorrect** TypeScript code for this rule:
83+
84+
::: incorrect
85+
86+
```ts
87+
/* eslint no-useless-constructor: "error" */
88+
89+
class A {
90+
public constructor() {}
91+
}
92+
```
93+
94+
:::
95+
96+
Examples of **correct** TypeScript code for this rule:
97+
98+
::: correct
99+
100+
```ts
101+
/* eslint no-useless-constructor: "error" */
102+
103+
class A {
104+
protected constructor() {}
105+
}
106+
107+
class B extends A {
108+
public constructor() {
109+
super();
110+
}
111+
}
112+
113+
class C {
114+
constructor(@decorated param) {}
115+
}
116+
```
117+
80118
## When Not To Use It
81119

82120
If you don't want to be notified about unnecessary constructors, you can safely disable this rule.

lib/rules/no-useless-constructor.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,35 @@ const astUtils = require("./utils/ast-utils");
1010
// Helpers
1111
//------------------------------------------------------------------------------
1212

13+
/**
14+
* Checks whether any of a method's parameters have a decorator or are a parameter property.
15+
* @param {ASTNode} node A method definition node.
16+
* @returns {boolean} `true` if any parameter had a decorator or is a parameter property.
17+
*/
18+
function hasDecoratorsOrParameterProperty(node) {
19+
return node.value.params.some(
20+
param =>
21+
param.decorators?.length || param.type === "TSParameterProperty",
22+
);
23+
}
24+
25+
/**
26+
* Checks whether a node's accessibility makes it not useless.
27+
* @param {ASTNode} node A method definition node.
28+
* @returns {boolean} `true` if the node has a useful accessibility.
29+
*/
30+
function hasUsefulAccessibility(node) {
31+
switch (node.accessibility) {
32+
case "protected":
33+
case "private":
34+
return true;
35+
case "public":
36+
return !!node.parent.parent.superClass;
37+
default:
38+
return false;
39+
}
40+
}
41+
1342
/**
1443
* Checks whether a given array of statements is a single call of `super`.
1544
* @param {ASTNode[]} body An array of statements to check.
@@ -135,6 +164,8 @@ function isRedundantSuperCall(body, ctorParams) {
135164
/** @type {import('../shared/types').Rule} */
136165
module.exports = {
137166
meta: {
167+
dialects: ["javascript", "typescript"],
168+
language: "javascript",
138169
type: "suggestion",
139170

140171
docs: {
@@ -160,7 +191,12 @@ module.exports = {
160191
* @returns {void}
161192
*/
162193
function checkForConstructor(node) {
163-
if (node.kind !== "constructor") {
194+
if (
195+
node.kind !== "constructor" ||
196+
node.value.type !== "FunctionExpression" ||
197+
hasDecoratorsOrParameterProperty(node) ||
198+
hasUsefulAccessibility(node)
199+
) {
164200
return;
165201
}
166202

tests/lib/rules/no-useless-constructor.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,169 @@ ruleTester.run("no-useless-constructor", rule, {
199199
},
200200
],
201201
});
202+
203+
const ruleTesterTypeScript = new RuleTester({
204+
languageOptions: {
205+
parser: require("@typescript-eslint/parser"),
206+
},
207+
});
208+
209+
ruleTesterTypeScript.run("no-useless-constructor", rule, {
210+
valid: [
211+
`
212+
declare class A {
213+
constructor();
214+
}
215+
`,
216+
`
217+
class A {
218+
constructor();
219+
}
220+
`,
221+
`
222+
abstract class A {
223+
constructor();
224+
}
225+
`,
226+
`
227+
class A {
228+
constructor(private name: string) {}
229+
}
230+
`,
231+
`
232+
class A {
233+
constructor(public name: string) {}
234+
}
235+
`,
236+
`
237+
class A {
238+
constructor(protected name: string) {}
239+
}
240+
`,
241+
`
242+
class A {
243+
private constructor() {}
244+
}
245+
`,
246+
`
247+
class A {
248+
protected constructor() {}
249+
}
250+
`,
251+
`
252+
class A extends B {
253+
public constructor() {}
254+
}
255+
`,
256+
`
257+
class A extends B {
258+
public constructor() {
259+
super();
260+
}
261+
}
262+
`,
263+
`
264+
class A extends B {
265+
protected constructor(foo, bar) {
266+
super(bar);
267+
}
268+
}
269+
`,
270+
`
271+
class A extends B {
272+
private constructor(foo, bar) {
273+
super(bar);
274+
}
275+
}
276+
`,
277+
`
278+
class A extends B {
279+
public constructor(foo) {
280+
super(foo);
281+
}
282+
}
283+
`,
284+
`
285+
class A extends B {
286+
public constructor(foo) {}
287+
}
288+
`,
289+
`
290+
class A {
291+
constructor(foo);
292+
}
293+
`,
294+
`
295+
class A {
296+
constructor(@Foo foo) {}
297+
}
298+
`,
299+
`
300+
class A {
301+
constructor(@Foo foo: string) {}
302+
}
303+
`,
304+
`
305+
class A extends Object {
306+
constructor(@Foo foo: string) {
307+
super(foo);
308+
}
309+
}
310+
`,
311+
`
312+
class A extends Object {
313+
constructor(foo: string, @Bar() bar) {
314+
super(foo, bar);
315+
}
316+
}
317+
`,
318+
],
319+
invalid: [
320+
{
321+
code: `
322+
class A {
323+
constructor() {}
324+
}
325+
`,
326+
errors: [
327+
{
328+
messageId: "noUselessConstructor",
329+
suggestions: [
330+
{
331+
messageId: "removeConstructor",
332+
output: `
333+
class A {
334+
${" "}
335+
}
336+
`,
337+
},
338+
],
339+
type: "MethodDefinition",
340+
},
341+
],
342+
},
343+
{
344+
code: `
345+
class A {
346+
public constructor() {}
347+
}
348+
`,
349+
errors: [
350+
{
351+
messageId: "noUselessConstructor",
352+
suggestions: [
353+
{
354+
messageId: "removeConstructor",
355+
output: `
356+
class A {
357+
${" "}
358+
}
359+
`,
360+
},
361+
],
362+
type: "MethodDefinition",
363+
},
364+
],
365+
},
366+
],
367+
});

0 commit comments

Comments
 (0)