Skip to content

Commit

Permalink
Add no-this-assignment rule (#1018)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
fisker and sindresorhus committed Jan 15, 2021
1 parent 8606a13 commit 62a2f92
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 0 deletions.
52 changes: 52 additions & 0 deletions docs/rules/no-this-assignment.md
@@ -0,0 +1,52 @@
# Disallow assigning `this` to a variable

`this` should be used directly. If you want a reference to `this` from a higher scope, consider using [arrow function expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) or [`Function#bind()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind).

## Fail

```js
const foo = this;

setTimeout(function () {
foo.bar();
}, 1000);
```

```js
const foo = this;

class Bar {
method() {
foo.baz();
}
}

new Bar().method();
```

## Pass

```js
setTimeout(() => {
this.bar();
}, 1000);
```

```js
setTimeout(function () {
this.bar();
}.bind(this), 1000);
```

```js
class Bar {
constructor(fooInstance) {
this.fooInstance = fooInstance;
}
method() {
this.fooInstance.baz();
}
}

new Bar(this).method();
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -70,6 +70,7 @@ module.exports = {
'unicorn/no-null': 'error',
'unicorn/no-object-as-default-parameter': 'error',
'unicorn/no-process-exit': 'error',
'unicorn/no-this-assignment': 'error',
'unicorn/no-unreadable-array-destructuring': 'error',
'unicorn/no-unsafe-regex': 'off',
'unicorn/no-unused-properties': 'off',
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Expand Up @@ -64,6 +64,7 @@ Configure it in `package.json`.
"unicorn/no-null": "error",
"unicorn/no-object-as-default-parameter": "error",
"unicorn/no-process-exit": "error",
"unicorn/no-this-assignment": "error",
"unicorn/no-unreadable-array-destructuring": "error",
"unicorn/no-unsafe-regex": "off",
"unicorn/no-unused-properties": "off",
Expand Down Expand Up @@ -140,6 +141,7 @@ Configure it in `package.json`.
- [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-this-assignment](docs/rules/no-this-assignment.md) - Disallow assigning `this` to a variable.
- [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) - Disallow unreadable array destructuring. *(partly fixable)*
- [no-unsafe-regex](docs/rules/no-unsafe-regex.md) - Disallow unsafe regular expressions.
- [no-unused-properties](docs/rules/no-unused-properties.md) - Disallow unused object properties.
Expand Down
43 changes: 43 additions & 0 deletions rules/no-this-assignment.js
@@ -0,0 +1,43 @@
'use strict';
const getDocumentationUrl = require('./utils/get-documentation-url');

const MESSAGE_ID = 'no-this-assignment';
const messages = {
[MESSAGE_ID]: 'Do not assign `this` to `{{name}}`.'
};

const variableDeclaratorSelector = [
'VariableDeclarator',
'[init.type="ThisExpression"]',
'[id.type="Identifier"]'
].join('');

const assignmentExpressionSelector = [
'AssignmentExpression',
'[right.type="ThisExpression"]',
'[left.type="Identifier"]'
].join('');

const selector = `:matches(${variableDeclaratorSelector}, ${assignmentExpressionSelector})`;

const create = context => ({
[selector](node) {
const variable = node.type === 'AssignmentExpression' ? node.left : node.id;
context.report({
node,
data: {name: variable.name},
messageId: MESSAGE_ID
});
}
});

module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
url: getDocumentationUrl(__filename)
},
messages
}
};
46 changes: 46 additions & 0 deletions test/no-this-assignment.js
@@ -0,0 +1,46 @@
import {outdent} from 'outdent';
import {test} from './utils/test.js';

test.visualize({
valid: [
'const {property} = this;',
'const property = this.property;',
'const [element] = this;',
'const element = this[0];',
'([element] = this);',
'element = this[0];',
'property = this.property;',
'const [element] = [this];',
'([element] = [this]);',
'const {property} = {property: this};',
'({property} = {property: this});',
'const self = true && this;',
'const self = false || this;',
'const self = false ?? this;',
'foo.bar = this;',
'function foo(a = this) {}',
'function foo({a = this}) {}',
'function foo([a = this]) {}'
],
invalid: [
'const foo = this;',
'let foo;foo = this;',
'var foo = bar, baz = this;'
]
});

test.babel({
valid: [
outdent`
class A {
foo = this;
}
`,
outdent`
class A {
static foo = this;
}
`
],
invalid: []
});
35 changes: 35 additions & 0 deletions test/snapshots/no-this-assignment.js.md
@@ -0,0 +1,35 @@
# Snapshot report for `test/no-this-assignment.js`

The actual snapshot is saved in `no-this-assignment.js.snap`.

Generated by [AVA](https://avajs.dev).

## Invalid #1
1 | const foo = this;

> Error 1/1
`␊
> 1 | const foo = this;␊
| ^^^^^^^^^^ Do not assign `this` to `foo`.␊
`

## Invalid #2
1 | let foo;foo = this;

> Error 1/1
`␊
> 1 | let foo;foo = this;␊
| ^^^^^^^^^^ Do not assign `this` to `foo`.␊
`

## Invalid #3
1 | var foo = bar, baz = this;

> Error 1/1
`␊
> 1 | var foo = bar, baz = this;␊
| ^^^^^^^^^^ Do not assign `this` to `baz`.␊
`
Binary file added test/snapshots/no-this-assignment.js.snap
Binary file not shown.

0 comments on commit 62a2f92

Please sign in to comment.