Skip to content

Commit

Permalink
Add no-object-as-default-parameter rule (#633)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
Co-authored-by: fisker <lionkay@gmail.com>
  • Loading branch information
3 people committed Jun 30, 2020
1 parent 1dca2f3 commit 9989d2d
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 0 deletions.
29 changes: 29 additions & 0 deletions docs/rules/no-object-as-default-parameter.md
@@ -0,0 +1,29 @@
# Disallow the use of objects as default parameters

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.


## Fail

```js
const abc = (foo = {a: false}) => {};
```

```js
const abc = (foo = {a: false, b: 123}) => {};
```


## Pass

```js
const abc = (foo = {}) => {};
```

```js
const abc = (foo = false) => {};
```

```js
const foo = ({a = false, b = 123}) => {};
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -39,6 +39,7 @@ module.exports = {
'unicorn/no-nested-ternary': 'error',
'unicorn/no-new-buffer': 'error',
'unicorn/no-null': 'error',
'unicorn/no-object-as-default-parameter': 'error',
'unicorn/no-process-exit': 'error',
'unicorn/no-reduce': 'error',
'unicorn/no-unreadable-array-destructuring': 'error',
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Expand Up @@ -55,6 +55,7 @@ Configure it in `package.json`.
"unicorn/no-nested-ternary": "error",
"unicorn/no-new-buffer": "error",
"unicorn/no-null": "error",
"unicorn/no-object-as-default-parameter": "error",
"unicorn/no-process-exit": "error",
"unicorn/no-reduce": "error",
"unicorn/no-unreadable-array-destructuring": "error",
Expand Down Expand Up @@ -116,6 +117,7 @@ Configure it in `package.json`.
- [no-nested-ternary](docs/rules/no-nested-ternary.md) - Disallow nested ternary expressions. *(partly fixable)*
- [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)*
- [no-null](docs/rules/no-null.md) - Disallow the use of the `null` literal.
- [no-object-as-default-parameter](docs/rules/no-object-as-default-parameter.md) - Disallow the use of objects as default parameters.
- [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`.
- [no-reduce](docs/rules/no-reduce.md) - Disallow `Array#reduce()` and `Array#reduceRight()`.
- [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) - Disallow unreadable array destructuring.
Expand Down
36 changes: 36 additions & 0 deletions rules/no-object-as-default-parameter.js
@@ -0,0 +1,36 @@
'use strict';
const getDocumentationUrl = require('./utils/get-documentation-url');

const MESSAGE_ID = 'noObjectAsDefaultParameter';

const objectParameterSelector = [
':function > AssignmentPattern.params',
'[left.type="Identifier"]',
'[right.type="ObjectExpression"]',
'[right.properties.length>0]'
].join('');

const create = context => {
return {
[objectParameterSelector]: node => {
context.report({
node: node.left,
messageId: MESSAGE_ID,
data: {parameter: node.left.name}
});
}
};
};

module.exports = {
create,
meta: {
type: 'problem',
docs: {
url: getDocumentationUrl(__filename)
},
messages: {
[MESSAGE_ID]: 'Do not use an object literal as default for parameter `{{parameter}}`.'
}
}
};
177 changes: 177 additions & 0 deletions test/no-object-as-default-parameter.js
@@ -0,0 +1,177 @@
import test from 'ava';
import avaRuleTester from 'eslint-ava-rule-tester';
import {outdent} from 'outdent';
import rule from '../rules/no-object-as-default-parameter';

const ruleTester = avaRuleTester(test, {
parserOptions: {
ecmaVersion: 2020
}
});

const error = {
messageId: 'noObjectAsDefaultParameter',
data: {parameter: 'foo'}
};

ruleTester.run('no-object-as-default-parameter', rule, {
valid: [
'const abc = {};',
'const abc = {foo: 123};',
'function abc(foo) {}',
'function abc(foo = null) {}',
'function abc(foo = undefined) {}',
'function abc(foo = 123) {}',
'function abc(foo = true) {}',
'function abc(foo = "bar") {}',
'function abc(foo = 123, bar = "foo") {}',
'function abc(foo = {}) {}',
'function abc({foo = 123} = {}) {}',
'(function abc() {})(foo = {a: 123})',
'const abc = foo => {};',
'const abc = (foo = null) => {};',
'const abc = (foo = undefined) => {};',
'const abc = (foo = 123) => {};',
'const abc = (foo = true) => {};',
'const abc = (foo = "bar") => {};',
'const abc = (foo = 123, bar = "foo") => {};',
'const abc = (foo = {}) => {};',
'const abc = ({a = true, b = "foo"}) => {};',
'const abc = function(foo = 123) {}',
'const {abc = {foo: 123}} = bar;',
'const {abc = {null: "baz"}} = bar;',
'const {abc = {foo: undefined}} = undefined;',
'const abc = ([{foo = false, bar = 123}]) => {};',
'const abc = ({foo = {a: 123}}) => {};',
'const abc = ([foo = {a: 123}]) => {};',
'const abc = ({foo: bar = {a: 123}}) => {};',
'const abc = () => (foo = {a: 123});',
outdent`
class A {
[foo = {a: 123}]() {}
}
`,
outdent`
class A extends (foo = {a: 123}) {
a() {}
}
`
],
invalid: [
{
code: 'function abc(foo = {a: 123}) {}',
errors: [error]
},
{
code: 'async function * abc(foo = {a: 123}) {}',
errors: [error]
},
{
code: 'function abc(foo = {a: false}) {}',
errors: [error]
},
{
code: 'function abc(foo = {a: "bar"}) {}',
errors: [error]
},
{
code: 'function abc(foo = {a: "bar", b: {c: true}}) {}',
errors: [error]
},
{
code: 'const abc = (foo = {a: false}) => {};',
errors: [error]
},
{
code: 'const abc = (foo = {a: 123, b: false}) => {};',
errors: [error]
},
{
code: 'const abc = (foo = {a: false, b: 1, c: "test", d: null}) => {};',
errors: [error]
},
{
code: 'const abc = function(foo = {a: 123}) {}',
errors: [error]
},
{
code: outdent`
class A {
abc(foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
class A {
constructor(foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
class A {
set abc(foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
class A {
static abc(foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
class A {
* abc(foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
class A {
static async * abc(foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
class A {
[foo = {a: 123}](foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
const A = class {
abc(foo = {a: 123}) {}
}
`,
errors: [error]
},
{
code: outdent`
object = {
abc(foo = {a: 123}) {}
};
`,
errors: [error]
},
// Actual message
{
code: 'function abc(foo = {a: 123}) {}',
errors: [{
message: 'Do not use an object literal as default for parameter `foo`.'
}]
}
]
});

0 comments on commit 9989d2d

Please sign in to comment.