Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3b931ac

Browse files
feat(eslint-plugin): [no-empty-func] private/protected construct (typescript-eslint#1267)
Co-authored-by: Brad Zacher <[email protected]>
1 parent c72c3c1 commit 3b931ac

File tree

3 files changed

+164
-29
lines changed

3 files changed

+164
-29
lines changed

packages/eslint-plugin/docs/rules/no-empty-function.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,37 @@ See the [ESLint documentation](https://eslint.org/docs/rules/no-empty-function)
4444
}
4545
```
4646

47+
## Options
48+
49+
This rule has an object option:
50+
51+
- `allow` (`string[]`)
52+
- `"protected-constructors"` - Protected class constructors.
53+
- `"private-constructors"` - Private class constructors.
54+
- [See the other options allowed](https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-function.md#options)
55+
56+
#### allow: protected-constructors
57+
58+
Examples of **correct** code for the `{ "allow": ["protected-constructors"] }` option:
59+
60+
```ts
61+
/*eslint @typescript-eslint/no-empty-function: ["error", { "allow": ["protected-constructors"] }]*/
62+
63+
class Foo {
64+
protected constructor() {}
65+
}
66+
```
67+
68+
#### allow: private-constructors
69+
70+
Examples of **correct** code for the `{ "allow": ["private-constructors"] }` option:
71+
72+
```ts
73+
/*eslint @typescript-eslint/no-empty-function: ["error", { "allow": ["private-constructors"] }]*/
74+
75+
class Foo {
76+
private constructor() {}
77+
}
78+
```
79+
4780
<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-function.md)</sup>

packages/eslint-plugin/src/rules/no-empty-function.ts

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,32 @@ import * as util from '../util';
88
type Options = util.InferOptionsTypeFromRule<typeof baseRule>;
99
type MessageIds = util.InferMessageIdsTypeFromRule<typeof baseRule>;
1010

11+
const schema = util.deepMerge(
12+
Array.isArray(baseRule.meta.schema)
13+
? baseRule.meta.schema[0]
14+
: baseRule.meta.schema,
15+
{
16+
properties: {
17+
allow: {
18+
items: {
19+
enum: [
20+
'functions',
21+
'arrowFunctions',
22+
'generatorFunctions',
23+
'methods',
24+
'generatorMethods',
25+
'getters',
26+
'setters',
27+
'constructors',
28+
'private-constructors',
29+
'protected-constructors',
30+
],
31+
},
32+
},
33+
},
34+
},
35+
);
36+
1137
export default util.createRule<Options, MessageIds>({
1238
name: 'no-empty-function',
1339
meta: {
@@ -17,32 +43,21 @@ export default util.createRule<Options, MessageIds>({
1743
category: 'Best Practices',
1844
recommended: 'error',
1945
},
20-
schema: baseRule.meta.schema,
46+
schema: [schema],
2147
messages: baseRule.meta.messages,
2248
},
2349
defaultOptions: [
2450
{
2551
allow: [],
2652
},
2753
],
28-
create(context) {
54+
create(context, [{ allow = [] }]) {
2955
const rules = baseRule.create(context);
3056

31-
/**
32-
* Checks if the node is a constructor
33-
* @param node the node to ve validated
34-
* @returns true if the node is a constructor
35-
* @private
36-
*/
37-
function isConstructor(
38-
node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression,
39-
): boolean {
40-
return !!(
41-
node.parent &&
42-
node.parent.type === 'MethodDefinition' &&
43-
node.parent.kind === 'constructor'
44-
);
45-
}
57+
const isAllowedProtectedConstructors = allow.includes(
58+
'protected-constructors',
59+
);
60+
const isAllowedPrivateConstructors = allow.includes('private-constructors');
4661

4762
/**
4863
* Check if the method body is empty
@@ -74,30 +89,44 @@ export default util.createRule<Options, MessageIds>({
7489
}
7590

7691
/**
77-
* Checks if the method is a concise constructor (no function body, but has parameter properties)
7892
* @param node the node to be validated
79-
* @returns true if the method is a concise constructor
93+
* @returns true if the constructor is allowed to be empty
8094
* @private
8195
*/
82-
function isConciseConstructor(
96+
function isAllowedEmptyConstructor(
8397
node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression,
8498
): boolean {
85-
// Check TypeScript specific nodes
86-
return (
87-
isConstructor(node) && isBodyEmpty(node) && hasParameterProperties(node)
88-
);
99+
const parent = node.parent;
100+
if (
101+
isBodyEmpty(node) &&
102+
parent?.type === 'MethodDefinition' &&
103+
parent.kind === 'constructor'
104+
) {
105+
const { accessibility } = parent;
106+
107+
return (
108+
// allow protected constructors
109+
(accessibility === 'protected' && isAllowedProtectedConstructors) ||
110+
// allow private constructors
111+
(accessibility === 'private' && isAllowedPrivateConstructors) ||
112+
// allow constructors which have parameter properties
113+
hasParameterProperties(node)
114+
);
115+
}
116+
117+
return false;
89118
}
90119

91120
return {
92121
FunctionDeclaration(node): void {
93-
if (!isConciseConstructor(node)) {
94-
rules.FunctionDeclaration(node);
95-
}
122+
rules.FunctionDeclaration(node);
96123
},
97124
FunctionExpression(node): void {
98-
if (!isConciseConstructor(node)) {
99-
rules.FunctionExpression(node);
125+
if (isAllowedEmptyConstructor(node)) {
126+
return;
100127
}
128+
129+
rules.FunctionExpression(node);
101130
},
102131
};
103132
},

packages/eslint-plugin/tests/rules/no-empty-function.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,29 @@ ruleTester.run('no-empty-function', rule, {
3232
}`,
3333
options: [{ allow: ['methods'] }],
3434
},
35+
{
36+
code: `
37+
class Foo {
38+
private constructor() {}
39+
}
40+
`,
41+
options: [{ allow: ['private-constructors'] }],
42+
},
43+
{
44+
code: `
45+
class Foo {
46+
protected constructor() {}
47+
}
48+
`,
49+
options: [{ allow: ['protected-constructors'] }],
50+
},
51+
{
52+
code: `
53+
function foo() {
54+
const a = null;
55+
}
56+
`,
57+
},
3558
],
3659

3760
invalid: [
@@ -65,5 +88,55 @@ ruleTester.run('no-empty-function', rule, {
6588
},
6689
],
6790
},
91+
{
92+
code: `
93+
class Foo {
94+
private constructor() {}
95+
}
96+
`,
97+
errors: [
98+
{
99+
messageId: 'unexpected',
100+
data: {
101+
name: 'constructor',
102+
},
103+
line: 3,
104+
column: 25,
105+
},
106+
],
107+
},
108+
{
109+
code: `
110+
class Foo {
111+
protected constructor() {}
112+
}
113+
`,
114+
errors: [
115+
{
116+
messageId: 'unexpected',
117+
data: {
118+
name: 'constructor',
119+
},
120+
line: 3,
121+
column: 27,
122+
},
123+
],
124+
},
125+
{
126+
code: `
127+
function foo() {
128+
}
129+
`,
130+
errors: [
131+
{
132+
messageId: 'unexpected',
133+
data: {
134+
name: "function 'foo'",
135+
},
136+
line: 2,
137+
column: 16,
138+
},
139+
],
140+
},
68141
],
69142
});

0 commit comments

Comments
 (0)