Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9a2d3bc

Browse files
j-f1JamesHenry
authored andcommitted
[FEAT] [no-unnecessary-class] Add rule (typescript-eslint#234)
* Add `no-unnecessary-class` rule* Add metadata* ⬆️ eslint-docs
1 parent347c543 commit9a2d3bc

File tree

6 files changed

+298
-5
lines changed

6 files changed

+298
-5
lines changed

‎packages/eslint-plugin-typescript/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ This guarantees 100% compatibility between the plugin and the parser.
4848

4949
<!-- Please run `npm run docs` to update this section-->
5050
<!-- begin rule list-->
51+
5152
**Key**::heavy_check_mark: = recommended,:wrench: = fixable
5253

54+
<!-- prettier-ignore-->
5355
| Name| Description|:heavy_check_mark:|:wrench:|
5456
| ----| -----------| ------------------| --------|
5557
|[`typescript/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md)| Require that member overloads be consecutive (`adjacent-overload-signatures` from TSLint)|||
@@ -68,6 +70,7 @@ This guarantees 100% compatibility between the plugin and the parser.
6870
|[`typescript/no-array-constructor`](./docs/rules/no-array-constructor.md)| Disallow generic`Array` constructors||:wrench:|
6971
|[`typescript/no-empty-interface`](./docs/rules/no-empty-interface.md)| Disallow the declaration of empty interfaces (`no-empty-interface` from TSLint)|||
7072
|[`typescript/no-explicit-any`](./docs/rules/no-explicit-any.md)| Disallow usage of the`any` type (`no-any` from TSLint)|||
73+
|[`typescript/no-extraneous-class`](./docs/rules/no-extraneous-class.md)| Forbids the use of classes as namespaces (`no-unnecessary-class` from TSLint)|||
7174
|[`typescript/no-inferrable-types`](./docs/rules/no-inferrable-types.md)| Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean. (`no-inferrable-types` from TSLint)||:wrench:|
7275
|[`typescript/no-misused-new`](./docs/rules/no-misused-new.md)| Enforce valid definition of`new` and`constructor`. (`no-misused-new` from TSLint)|||
7376
|[`typescript/no-namespace`](./docs/rules/no-namespace.md)| Disallow the use of custom TypeScript modules and namespaces (`no-namespace` from TSLint)|||
@@ -82,4 +85,5 @@ This guarantees 100% compatibility between the plugin and the parser.
8285
|[`typescript/prefer-interface`](./docs/rules/prefer-interface.md)| Prefer an interface declaration over a type literal (type T = { ... }) (`interface-over-type-literal` from TSLint)||:wrench:|
8386
|[`typescript/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md)| Require the use of the`namespace` keyword instead of the`module` keyword to declare custom TypeScript modules. (`no-internal-module` from TSLint)||:wrench:|
8487
|[`typescript/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md)| Require consistent spacing around type annotations (`typedef-whitespace` from TSLint)||:wrench:|
88+
8589
<!-- end rule list-->
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#Forbids the use of classes as namespaces (no-extraneous-class)
2+
3+
This rule warns when a class is accidentally used as a namespace.
4+
5+
##Rule Details
6+
7+
From TSLint’s docs:
8+
9+
>Users who come from a Java-style OO language may wrap their utility functions in an extra class,
10+
>instead of putting them at the top level.
11+
12+
Examples of**incorrect** code for this rule:
13+
14+
```ts
15+
classEmptyClass {}
16+
17+
classConstructorOnly {
18+
constructor() {
19+
foo();
20+
}
21+
}
22+
23+
// Use an object instead:
24+
classStaticOnly {
25+
static version=42;
26+
static hello() {
27+
console.log("Hello, world!");
28+
}
29+
}
30+
```
31+
32+
Examples of**correct** code for this rule:
33+
34+
```ts
35+
classEmptyClassextendsSuperClass {}
36+
37+
classParameterProperties {
38+
constructor(publicname:string) {}
39+
}
40+
41+
const StaticOnly= {
42+
version:42,
43+
hello() {
44+
console.log("Hello, world!");
45+
},
46+
};
47+
```
48+
49+
###Options
50+
51+
This rule accepts a single object option.
52+
53+
-`constructorOnly: true` will silence warnings about classes containing only a constructor.
54+
-`allowEmpty: true` will silence warnings about empty classes.
55+
-`staticOnly: true` will silence warnings about classes containing only static members.
56+
57+
##When Not To Use It
58+
59+
You can disable this rule if you don’t have anyone who would make these kinds of mistakes on your
60+
team or if you use classes as namespaces.
61+
62+
##Compatibility
63+
64+
[`no-unnecessary-class`](https://palantir.github.io/tslint/rules/no-unnecessary-class/) from TSLint
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
*@fileoverview Forbids the use of classes as namespaces
3+
*@author Jed Fox
4+
*/
5+
"use strict";
6+
7+
constutil=require("../util");
8+
9+
//------------------------------------------------------------------------------
10+
// Rule Definition
11+
//------------------------------------------------------------------------------
12+
13+
module.exports={
14+
meta:{
15+
docs:{
16+
description:"Forbids the use of classes as namespaces",
17+
extraDescription:[util.tslintRule("no-unnecessary-class")],
18+
category:"Best Practices",
19+
recommended:false,
20+
url:util.metaDocsUrl("no-extraneous-class"),
21+
},
22+
fixable:null,
23+
schema:[
24+
{
25+
type:"object",
26+
additionalProperties:false,
27+
properties:{
28+
allowConstructorOnly:{
29+
type:"boolean",
30+
},
31+
allowEmpty:{
32+
type:"boolean",
33+
},
34+
allowStaticOnly:{
35+
type:"boolean",
36+
},
37+
},
38+
},
39+
],
40+
messages:{
41+
empty:"Unexpected empty class.",
42+
onlyStatic:"Unexpected class with only static properties.",
43+
onlyConstructor:"Unexpected class with only a constructor.",
44+
},
45+
},
46+
47+
create(context){
48+
const{ allowConstructorOnly, allowEmpty, allowStaticOnly}=
49+
context.options[0]||{};
50+
51+
return{
52+
ClassBody(node){
53+
const{ id, superClass}=node.parent;
54+
55+
if(superClass)return;
56+
57+
if(node.body.length===0){
58+
if(allowEmpty)return;
59+
context.report({node:id,messageId:"empty"});
60+
return;
61+
}
62+
63+
letonlyStatic=true;
64+
letonlyConstructor=true;
65+
66+
for(constpropofnode.body){
67+
if(prop.kind==="constructor"){
68+
if(
69+
prop.value.params.some(
70+
param=>param.type==="TSParameterProperty"
71+
)
72+
){
73+
onlyConstructor=false;
74+
onlyStatic=false;
75+
}
76+
}else{
77+
onlyConstructor=false;
78+
if(!prop.static){
79+
onlyStatic=false;
80+
}
81+
}
82+
if(!(onlyStatic||onlyConstructor))break;
83+
}
84+
85+
if(onlyConstructor){
86+
if(!allowConstructorOnly){
87+
context.report({
88+
node:id,
89+
messageId:"onlyConstructor",
90+
});
91+
}
92+
return;
93+
}
94+
if(onlyStatic&&!allowStaticOnly){
95+
context.report({node:id,messageId:"onlyStatic"});
96+
}
97+
},
98+
};
99+
},
100+
};

‎packages/eslint-plugin-typescript/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"eslint":"^5.9.0",
2929
"eslint-config-eslint":"^5.0.1",
3030
"eslint-config-prettier":"^3.3.0",
31-
"eslint-docs":"^0.2.3",
31+
"eslint-docs":"^0.2.6",
3232
"eslint-plugin-eslint-plugin":"^2.0.0",
3333
"eslint-plugin-node":"^8.0.0",
3434
"eslint-plugin-prettier":"^3.0.0",
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
*@fileoverview Forbids the use of classes as namespaces
3+
* Some tests adapted from https://github.com/palantir/tslint/tree/c7fc99b5/test/rules/no-unnecessary-class
4+
*@author Jed Fox
5+
*/
6+
"use strict";
7+
8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
construle=require("../../../lib/rules/no-extraneous-class"),
13+
RuleTester=require("eslint").RuleTester;
14+
15+
constempty={messageId:"empty",type:"Identifier"};
16+
constonlyStatic={messageId:"onlyStatic",type:"Identifier"};
17+
constonlyConstructor={messageId:"onlyConstructor",type:"Identifier"};
18+
19+
//------------------------------------------------------------------------------
20+
// Tests
21+
//------------------------------------------------------------------------------
22+
23+
construleTester=newRuleTester({
24+
parser:"typescript-eslint-parser",
25+
});
26+
27+
ruleTester.run("no-extraneous-class",rule,{
28+
valid:[
29+
`
30+
class Foo {
31+
public prop = 1;
32+
constructor() {}
33+
}
34+
`.trim(),
35+
`
36+
export class CClass extends BaseClass {
37+
public static helper(): void {}
38+
private static privateHelper(): boolean {
39+
return true;
40+
}
41+
constructor() {}
42+
}
43+
`.trim(),
44+
`
45+
class Foo {
46+
constructor(
47+
public bar: string
48+
) {}
49+
}
50+
`.trim(),
51+
{
52+
code:"class Foo {}",
53+
options:[{allowEmpty:true}],
54+
},
55+
{
56+
code:`
57+
class Foo {
58+
constructor() {}
59+
}
60+
`.trim(),
61+
options:[{allowConstructorOnly:true}],
62+
},
63+
{
64+
code:`
65+
export class Bar {
66+
public static helper(): void {}
67+
private static privateHelper(): boolean {
68+
return true;
69+
}
70+
}
71+
`.trim(),
72+
options:[{allowStaticOnly:true}],
73+
},
74+
],
75+
76+
invalid:[
77+
{
78+
code:"class Foo {}",
79+
errors:[empty],
80+
},
81+
{
82+
code:`
83+
class Foo {
84+
public prop = 1;
85+
constructor() {
86+
class Bar {
87+
static PROP = 2;
88+
}
89+
}
90+
}
91+
export class Bar {
92+
public static helper(): void {}
93+
private static privateHelper(): boolean {
94+
return true;
95+
}
96+
}
97+
`.trim(),
98+
errors:[onlyStatic,onlyStatic],
99+
},
100+
{
101+
code:`
102+
class Foo {
103+
constructor() {}
104+
}
105+
`.trim(),
106+
errors:[onlyConstructor],
107+
},
108+
{
109+
code:`
110+
export class AClass {
111+
public static helper(): void {}
112+
private static privateHelper(): boolean {
113+
return true;
114+
}
115+
constructor() {
116+
class nestedClass {
117+
}
118+
}
119+
}
120+
121+
`.trim(),
122+
errors:[onlyStatic,empty],
123+
},
124+
],
125+
});

‎packages/eslint-plugin-typescript/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -707,10 +707,10 @@ eslint-config-prettier@^3.3.0:
707707
dependencies:
708708
get-stdin "^6.0.0"
709709

710-
eslint-docs@^0.2.3:
711-
version "0.2.3"
712-
resolved "https://registry.yarnpkg.com/eslint-docs/-/eslint-docs-0.2.3.tgz#71a583443c1d74bfc7c1af31b3249e258037309c"
713-
integrity sha512-6afUYuK65TOPliMul0yIGwCiKWNEqk54RbuNZhJKt1mTVHyG0D2Zbbpa7yTT1/TiCfnUkfQa7TA/l7Jgu998Dw==
710+
eslint-docs@^0.2.6:
711+
version "0.2.6"
712+
resolved "https://registry.yarnpkg.com/eslint-docs/-/eslint-docs-0.2.6.tgz#40bfbf62e22f948f02893e90280abf36ff260293"
713+
integrity sha512-XyuBu/5d51ZecfYY+cantvWDLloo5j38sHJZE3VNU0u4PJCaOYvscWXBX/9ADzXBDJF5TvBEXCqPDnK/WrOiTA==
714714
dependencies:
715715
chalk "^2.4.1"
716716
detect-newline "^2.1.0"

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp