Skip to content

Commit 9989d2d

Browse files
medusalixsindresorhusfisker
authored
Add no-object-as-default-parameter rule (#633)
Co-authored-by: Sindre Sorhus <[email protected]> Co-authored-by: fisker <[email protected]>
1 parent 1dca2f3 commit 9989d2d

File tree

5 files changed

+245
-0
lines changed

5 files changed

+245
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Disallow the use of objects as default parameters
2+
3+
Default parameters should not be passed to a function through an object literal. The `foo = {a: false}` parameter works fine if only used with one option. As soon as additional options are added, you risk replacing the whole `foo = {a: false, b: true}` object when passing only one option: `{a: true}`. For this reason, object destructuring should be used instead.
4+
5+
6+
## Fail
7+
8+
```js
9+
const abc = (foo = {a: false}) => {};
10+
```
11+
12+
```js
13+
const abc = (foo = {a: false, b: 123}) => {};
14+
```
15+
16+
17+
## Pass
18+
19+
```js
20+
const abc = (foo = {}) => {};
21+
```
22+
23+
```js
24+
const abc = (foo = false) => {};
25+
```
26+
27+
```js
28+
const foo = ({a = false, b = 123}) => {};
29+
```

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module.exports = {
3939
'unicorn/no-nested-ternary': 'error',
4040
'unicorn/no-new-buffer': 'error',
4141
'unicorn/no-null': 'error',
42+
'unicorn/no-object-as-default-parameter': 'error',
4243
'unicorn/no-process-exit': 'error',
4344
'unicorn/no-reduce': 'error',
4445
'unicorn/no-unreadable-array-destructuring': 'error',

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Configure it in `package.json`.
5555
"unicorn/no-nested-ternary": "error",
5656
"unicorn/no-new-buffer": "error",
5757
"unicorn/no-null": "error",
58+
"unicorn/no-object-as-default-parameter": "error",
5859
"unicorn/no-process-exit": "error",
5960
"unicorn/no-reduce": "error",
6061
"unicorn/no-unreadable-array-destructuring": "error",
@@ -116,6 +117,7 @@ Configure it in `package.json`.
116117
- [no-nested-ternary](docs/rules/no-nested-ternary.md) - Disallow nested ternary expressions. *(partly fixable)*
117118
- [no-new-buffer](docs/rules/no-new-buffer.md) - Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. *(fixable)*
118119
- [no-null](docs/rules/no-null.md) - Disallow the use of the `null` literal.
120+
- [no-object-as-default-parameter](docs/rules/no-object-as-default-parameter.md) - Disallow the use of objects as default parameters.
119121
- [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`.
120122
- [no-reduce](docs/rules/no-reduce.md) - Disallow `Array#reduce()` and `Array#reduceRight()`.
121123
- [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) - Disallow unreadable array destructuring.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
const getDocumentationUrl = require('./utils/get-documentation-url');
3+
4+
const MESSAGE_ID = 'noObjectAsDefaultParameter';
5+
6+
const objectParameterSelector = [
7+
':function > AssignmentPattern.params',
8+
'[left.type="Identifier"]',
9+
'[right.type="ObjectExpression"]',
10+
'[right.properties.length>0]'
11+
].join('');
12+
13+
const create = context => {
14+
return {
15+
[objectParameterSelector]: node => {
16+
context.report({
17+
node: node.left,
18+
messageId: MESSAGE_ID,
19+
data: {parameter: node.left.name}
20+
});
21+
}
22+
};
23+
};
24+
25+
module.exports = {
26+
create,
27+
meta: {
28+
type: 'problem',
29+
docs: {
30+
url: getDocumentationUrl(__filename)
31+
},
32+
messages: {
33+
[MESSAGE_ID]: 'Do not use an object literal as default for parameter `{{parameter}}`.'
34+
}
35+
}
36+
};
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import test from 'ava';
2+
import avaRuleTester from 'eslint-ava-rule-tester';
3+
import {outdent} from 'outdent';
4+
import rule from '../rules/no-object-as-default-parameter';
5+
6+
const ruleTester = avaRuleTester(test, {
7+
parserOptions: {
8+
ecmaVersion: 2020
9+
}
10+
});
11+
12+
const error = {
13+
messageId: 'noObjectAsDefaultParameter',
14+
data: {parameter: 'foo'}
15+
};
16+
17+
ruleTester.run('no-object-as-default-parameter', rule, {
18+
valid: [
19+
'const abc = {};',
20+
'const abc = {foo: 123};',
21+
'function abc(foo) {}',
22+
'function abc(foo = null) {}',
23+
'function abc(foo = undefined) {}',
24+
'function abc(foo = 123) {}',
25+
'function abc(foo = true) {}',
26+
'function abc(foo = "bar") {}',
27+
'function abc(foo = 123, bar = "foo") {}',
28+
'function abc(foo = {}) {}',
29+
'function abc({foo = 123} = {}) {}',
30+
'(function abc() {})(foo = {a: 123})',
31+
'const abc = foo => {};',
32+
'const abc = (foo = null) => {};',
33+
'const abc = (foo = undefined) => {};',
34+
'const abc = (foo = 123) => {};',
35+
'const abc = (foo = true) => {};',
36+
'const abc = (foo = "bar") => {};',
37+
'const abc = (foo = 123, bar = "foo") => {};',
38+
'const abc = (foo = {}) => {};',
39+
'const abc = ({a = true, b = "foo"}) => {};',
40+
'const abc = function(foo = 123) {}',
41+
'const {abc = {foo: 123}} = bar;',
42+
'const {abc = {null: "baz"}} = bar;',
43+
'const {abc = {foo: undefined}} = undefined;',
44+
'const abc = ([{foo = false, bar = 123}]) => {};',
45+
'const abc = ({foo = {a: 123}}) => {};',
46+
'const abc = ([foo = {a: 123}]) => {};',
47+
'const abc = ({foo: bar = {a: 123}}) => {};',
48+
'const abc = () => (foo = {a: 123});',
49+
outdent`
50+
class A {
51+
[foo = {a: 123}]() {}
52+
}
53+
`,
54+
outdent`
55+
class A extends (foo = {a: 123}) {
56+
a() {}
57+
}
58+
`
59+
],
60+
invalid: [
61+
{
62+
code: 'function abc(foo = {a: 123}) {}',
63+
errors: [error]
64+
},
65+
{
66+
code: 'async function * abc(foo = {a: 123}) {}',
67+
errors: [error]
68+
},
69+
{
70+
code: 'function abc(foo = {a: false}) {}',
71+
errors: [error]
72+
},
73+
{
74+
code: 'function abc(foo = {a: "bar"}) {}',
75+
errors: [error]
76+
},
77+
{
78+
code: 'function abc(foo = {a: "bar", b: {c: true}}) {}',
79+
errors: [error]
80+
},
81+
{
82+
code: 'const abc = (foo = {a: false}) => {};',
83+
errors: [error]
84+
},
85+
{
86+
code: 'const abc = (foo = {a: 123, b: false}) => {};',
87+
errors: [error]
88+
},
89+
{
90+
code: 'const abc = (foo = {a: false, b: 1, c: "test", d: null}) => {};',
91+
errors: [error]
92+
},
93+
{
94+
code: 'const abc = function(foo = {a: 123}) {}',
95+
errors: [error]
96+
},
97+
{
98+
code: outdent`
99+
class A {
100+
abc(foo = {a: 123}) {}
101+
}
102+
`,
103+
errors: [error]
104+
},
105+
{
106+
code: outdent`
107+
class A {
108+
constructor(foo = {a: 123}) {}
109+
}
110+
`,
111+
errors: [error]
112+
},
113+
{
114+
code: outdent`
115+
class A {
116+
set abc(foo = {a: 123}) {}
117+
}
118+
`,
119+
errors: [error]
120+
},
121+
{
122+
code: outdent`
123+
class A {
124+
static abc(foo = {a: 123}) {}
125+
}
126+
`,
127+
errors: [error]
128+
},
129+
{
130+
code: outdent`
131+
class A {
132+
* abc(foo = {a: 123}) {}
133+
}
134+
`,
135+
errors: [error]
136+
},
137+
{
138+
code: outdent`
139+
class A {
140+
static async * abc(foo = {a: 123}) {}
141+
}
142+
`,
143+
errors: [error]
144+
},
145+
{
146+
code: outdent`
147+
class A {
148+
[foo = {a: 123}](foo = {a: 123}) {}
149+
}
150+
`,
151+
errors: [error]
152+
},
153+
{
154+
code: outdent`
155+
const A = class {
156+
abc(foo = {a: 123}) {}
157+
}
158+
`,
159+
errors: [error]
160+
},
161+
{
162+
code: outdent`
163+
object = {
164+
abc(foo = {a: 123}) {}
165+
};
166+
`,
167+
errors: [error]
168+
},
169+
// Actual message
170+
{
171+
code: 'function abc(foo = {a: 123}) {}',
172+
errors: [{
173+
message: 'Do not use an object literal as default for parameter `foo`.'
174+
}]
175+
}
176+
]
177+
});

0 commit comments

Comments
 (0)