Skip to content

Commit f3e4783

Browse files
committed
feat: implement @typescript-eslint/no-misused-new
1 parent e62592b commit f3e4783

File tree

5 files changed

+448
-1
lines changed

5 files changed

+448
-1
lines changed

internal/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_implied_eval"
4040
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_invalid_void_type"
4141
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_meaningless_void_operator"
42+
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_misused_new"
4243
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_misused_promises"
4344
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_misused_spread"
4445
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_mixed_enums"
@@ -391,6 +392,7 @@ func registerAllTypeScriptEslintPluginRules() {
391392
GlobalRuleRegistry.Register("@typescript-eslint/no-for-in-array", no_for_in_array.NoForInArrayRule)
392393
GlobalRuleRegistry.Register("@typescript-eslint/no-implied-eval", no_implied_eval.NoImpliedEvalRule)
393394
GlobalRuleRegistry.Register("@typescript-eslint/no-meaningless-void-operator", no_meaningless_void_operator.NoMeaninglessVoidOperatorRule)
395+
GlobalRuleRegistry.Register("@typescript-eslint/no-misused-new", no_misused_new.NoMisusedNewRule)
394396
GlobalRuleRegistry.Register("@typescript-eslint/no-misused-promises", no_misused_promises.NoMisusedPromisesRule)
395397
GlobalRuleRegistry.Register("@typescript-eslint/no-misused-spread", no_misused_spread.NoMisusedSpreadRule)
396398
GlobalRuleRegistry.Register("@typescript-eslint/no-mixed-enums", no_mixed_enums.NoMixedEnumsRule)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package no_misused_new
2+
3+
import (
4+
"github.com/microsoft/typescript-go/shim/ast"
5+
"github.com/web-infra-dev/rslint/internal/rule"
6+
)
7+
8+
/**
9+
* check whether the name of the return type of the node is the same as the name of the parent node.
10+
*/
11+
func check(node *ast.Node) bool {
12+
parentName := node.Parent.Name()
13+
if parentName == nil {
14+
return false
15+
}
16+
17+
nodeType := node.Type()
18+
if nodeType != nil && ast.IsTypeReferenceNode(nodeType) {
19+
typeName := nodeType.AsTypeReference().TypeName
20+
if ast.IsIdentifier(typeName) {
21+
return typeName.Text() == parentName.Text()
22+
}
23+
}
24+
return false
25+
}
26+
27+
var NoMisusedNewRule = rule.CreateRule(rule.Rule{
28+
Name: "no-misused-new",
29+
Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
30+
return rule.RuleListeners{
31+
ast.KindMethodDeclaration: func(node *ast.Node) {
32+
parentKind := node.Parent.Kind
33+
if parentKind != ast.KindClassDeclaration && parentKind != ast.KindClassExpression {
34+
return
35+
}
36+
37+
if node.Name().Text() != "new" {
38+
return
39+
}
40+
// If the function body exists, it's valid for this rule.
41+
body := node.Body()
42+
if body != nil {
43+
return
44+
}
45+
46+
if check(node) {
47+
ctx.ReportNode(node, rule.RuleMessage{
48+
Id: "errorMessageClass",
49+
Description: "Class cannot have method named `new`.",
50+
})
51+
}
52+
},
53+
ast.KindConstructSignature: func(node *ast.Node) {
54+
if node.Parent.Kind != ast.KindInterfaceDeclaration {
55+
return
56+
}
57+
if check(node) {
58+
ctx.ReportNode(node, rule.RuleMessage{
59+
Id: "errorMessageInterface",
60+
Description: "interfaces cannot be constructed, only classes.",
61+
})
62+
}
63+
},
64+
ast.KindMethodSignature: func(node *ast.Node) {
65+
if node.Name().Text() == "constructor" {
66+
ctx.ReportNode(node, rule.RuleMessage{
67+
Id: "errorMessageInterface",
68+
Description: "interfaces cannot be constructed, only classes.",
69+
})
70+
}
71+
},
72+
}
73+
},
74+
})
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package no_misused_new
2+
3+
import (
4+
"testing"
5+
6+
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/fixtures"
7+
"github.com/web-infra-dev/rslint/internal/rule_tester"
8+
)
9+
10+
func TestNoMisusedNewRule(t *testing.T) {
11+
rule_tester.RunRuleTester(fixtures.GetRootDir(), "tsconfig.json", t, &NoMisusedNewRule, []rule_tester.ValidTestCase{
12+
{
13+
Code: `
14+
declare abstract class C {
15+
foo() {}
16+
get new();
17+
bar();
18+
}
19+
`,
20+
},
21+
{
22+
Code: `
23+
class C {
24+
constructor();
25+
}
26+
`,
27+
},
28+
{
29+
Code: `
30+
const foo = class {
31+
constructor();
32+
};
33+
`,
34+
},
35+
{
36+
Code: `
37+
const foo = class {
38+
new(): X;
39+
};
40+
`,
41+
},
42+
{
43+
Code: `
44+
class C {
45+
constructor() {}
46+
}
47+
`,
48+
},
49+
{
50+
Code: `
51+
const foo = class {
52+
new() {}
53+
};
54+
`,
55+
},
56+
{
57+
Code: `
58+
const foo = class {
59+
constructor() {}
60+
};
61+
`,
62+
},
63+
{
64+
Code: `
65+
interface I {
66+
new (): {};
67+
}
68+
`,
69+
},
70+
{
71+
Code: `type T = { new (): T };`,
72+
},
73+
{
74+
Code: `
75+
export default class {
76+
constructor();
77+
}
78+
`,
79+
},
80+
{
81+
Code: `
82+
interface foo {
83+
new <T>(): bar<T>;
84+
}
85+
`,
86+
},
87+
{
88+
Code: `
89+
interface foo {
90+
new <T>(): 'x';
91+
}
92+
`,
93+
},
94+
}, []rule_tester.InvalidTestCase{
95+
{
96+
Code: `
97+
interface I {
98+
new (): I;
99+
constructor(): void;
100+
}
101+
`,
102+
Errors: []rule_tester.InvalidTestCaseError{
103+
{
104+
MessageId: "errorMessageInterface",
105+
Line: 3,
106+
},
107+
{
108+
MessageId: "errorMessageInterface",
109+
Line: 4,
110+
},
111+
},
112+
},
113+
{
114+
Code: `
115+
interface G {
116+
new <T>(): G<T>;
117+
}
118+
`,
119+
Errors: []rule_tester.InvalidTestCaseError{
120+
{
121+
MessageId: "errorMessageInterface",
122+
},
123+
},
124+
},
125+
{
126+
Code: `
127+
type T = {
128+
constructor(): void;
129+
};
130+
`,
131+
Errors: []rule_tester.InvalidTestCaseError{
132+
{
133+
MessageId: "errorMessageInterface",
134+
},
135+
},
136+
},
137+
{
138+
Code: `
139+
class C {
140+
new(): C;
141+
}
142+
`,
143+
Errors: []rule_tester.InvalidTestCaseError{
144+
{
145+
MessageId: "errorMessageClass",
146+
},
147+
},
148+
},
149+
{
150+
Code: `
151+
declare abstract class C {
152+
new(): C;
153+
}
154+
`,
155+
Errors: []rule_tester.InvalidTestCaseError{
156+
{
157+
MessageId: "errorMessageClass",
158+
},
159+
},
160+
},
161+
{
162+
Code: `
163+
interface I {
164+
constructor(): '';
165+
}
166+
`,
167+
Errors: []rule_tester.InvalidTestCaseError{
168+
{
169+
MessageId: "errorMessageInterface",
170+
},
171+
},
172+
},
173+
})
174+
}

packages/rslint-test-tools/rstest.config.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export default defineConfig({
9393
// './tests/typescript-eslint/rules/no-loss-of-precision.test.ts',
9494
// './tests/typescript-eslint/rules/no-magic-numbers.test.ts',
9595
// './tests/typescript-eslint/rules/no-meaningless-void-operator.test.ts',
96-
// './tests/typescript-eslint/rules/no-misused-new.test.ts',
96+
'./tests/typescript-eslint/rules/no-misused-new.test.ts',
9797
// './tests/typescript-eslint/rules/no-misused-promises.test.ts',
9898
// './tests/typescript-eslint/rules/no-misused-spread.test.ts',
9999
// './tests/typescript-eslint/rules/no-mixed-enums.test.ts',

0 commit comments

Comments
 (0)