This repository was archived by the owner on Jan 19, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FEAT] [no-this-alias] Add rule (#232)
- Loading branch information
1 parent
84808c1
commit 0d6794d
Showing
4 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Disallow aliasing `this` (no-this-alias) | ||
|
||
This rule prohibts assigning variables to `this`. | ||
|
||
## Rule Details | ||
|
||
Rationale from TSLint: | ||
|
||
> Assigning a variable to `this` instead of properly using arrow lambdas may be a symptom of pre-ES6 practices | ||
> or not managing scope well. | ||
> | ||
> Instead of storing a reference to `this` and using it inside a `function () {`: | ||
> | ||
> ```js | ||
> const self = this; | ||
> | ||
> setTimeout(function () { | ||
> self.doWork(); | ||
> }); | ||
> ``` | ||
> | ||
> Use `() =>` arrow lambdas, as they preserve `this` scope for you: | ||
> | ||
> ```js | ||
> setTimeout(() => { | ||
> this.doWork(); | ||
> }); | ||
> ``` | ||
Examples of **incorrect** code for this rule: | ||
(see the rationale above) | ||
Examples of **correct** code for this rule: | ||
(see the rationale above) | ||
### Options | ||
You can pass an object option: | ||
```json5 | ||
{ | ||
"typescript/no-this-alias": ["error", { | ||
"allowDestructuring": true, // Allow `const { props, state } = this`; false by default | ||
"allowedNames": ["self"], // Allow `const self = this`; `[]` by default | ||
}], | ||
} | ||
``` | ||
## When Not To Use It | ||
|
||
If you need to assign `this` to variables, you shouldn’t use this rule. | ||
|
||
## Related to | ||
|
||
- TSLint: [`no-this-assignment`](https://palantir.github.io/tslint/rules/no-this-assignment/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/** | ||
* @fileoverview Disallow aliasing `this` | ||
* @author Jed Fox | ||
*/ | ||
"use strict"; | ||
|
||
const util = require("../util"); | ||
|
||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: "Disallow aliasing `this`", | ||
extraDescription: [util.tslintRule("no-this-assignment")], | ||
category: "Best Practices", | ||
recommended: false, | ||
url: util.metaDocsUrl("no-this-alias"), | ||
}, | ||
fixable: null, | ||
schema: [ | ||
{ | ||
type: "object", | ||
additionalProperties: false, | ||
properties: { | ||
allowDestructuring: { | ||
type: "boolean", | ||
}, | ||
allowedNames: { | ||
type: "array", | ||
items: { | ||
type: "string", | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
messages: { | ||
thisAssignment: "Unexpected aliasing of 'this' to local variable.", | ||
thisDestructure: | ||
"Unexpected aliasing of members of 'this' to local variables.", | ||
}, | ||
}, | ||
|
||
create(context) { | ||
const { allowDestructuring = false, allowedNames = [] } = | ||
context.options[0] || {}; | ||
|
||
return { | ||
VariableDeclarator(node) { | ||
const { id, init } = node; | ||
|
||
if (init.type !== "ThisExpression") return; | ||
if (allowDestructuring && node.id.type !== "Identifier") return; | ||
|
||
if (!allowedNames.includes(id.name)) { | ||
context.report({ | ||
node: id, | ||
messageId: | ||
id.type === "Identifier" | ||
? "thisAssignment" | ||
: "thisDestructure", | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/** | ||
* @fileoverview Disallow aliasing `this` | ||
* Some tests taken from TSLint: https://github.com/palantir/tslint/tree/c7fc99b5/test/rules/no-this-assignment | ||
* @author Jed Fox | ||
*/ | ||
"use strict"; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
|
||
const rule = require("../../../lib/rules/no-this-alias"), | ||
RuleTester = require("eslint").RuleTester; | ||
|
||
const idError = { messageId: "thisAssignment", type: "Identifier" }; | ||
const destructureError = { | ||
messageId: "thisDestructure", | ||
type: "ObjectPattern", | ||
}; | ||
const arrayDestructureError = { | ||
messageId: "thisDestructure", | ||
type: "ArrayPattern", | ||
}; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Tests | ||
//------------------------------------------------------------------------------ | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: "typescript-eslint-parser", | ||
}); | ||
|
||
ruleTester.run("no-this-alias", rule, { | ||
valid: [ | ||
"const self = foo(this);", | ||
{ | ||
code: ` | ||
const { props, state } = this; | ||
const { length } = this; | ||
const { length, toString } = this; | ||
const [foo] = this; | ||
const [foo, bar] = this; | ||
`.trim(), | ||
options: [ | ||
{ | ||
allowDestructuring: true, | ||
}, | ||
], | ||
}, | ||
{ | ||
code: "const self = this;", | ||
options: [ | ||
{ | ||
allowedNames: ["self"], | ||
}, | ||
], | ||
}, | ||
], | ||
|
||
invalid: [ | ||
{ | ||
code: "const self = this;", | ||
options: [ | ||
{ | ||
allowDestructuring: true, | ||
}, | ||
], | ||
errors: [idError], | ||
}, | ||
{ | ||
code: "const self = this;", | ||
errors: [idError], | ||
}, | ||
{ | ||
code: "const { props, state } = this;", | ||
errors: [destructureError], | ||
}, | ||
{ | ||
code: ` | ||
var unscoped = this; | ||
function testFunction() { | ||
let inFunction = this; | ||
} | ||
const testLambda = () => { | ||
const inLambda = this; | ||
}; | ||
`.trim(), | ||
errors: [idError, idError, idError], | ||
}, | ||
{ | ||
code: ` | ||
class TestClass { | ||
constructor() { | ||
const inConstructor = this; | ||
const asThis: this = this; | ||
const asString = "this"; | ||
const asArray = [this]; | ||
const asArrayString = ["this"]; | ||
} | ||
public act(scope: this = this) { | ||
const inMemberFunction = this; | ||
const { act } = this; | ||
const { act, constructor } = this; | ||
const [foo] = this; | ||
const [foo, bar] = this; | ||
} | ||
} | ||
`.trim(), | ||
errors: [ | ||
idError, | ||
idError, | ||
idError, | ||
destructureError, | ||
destructureError, | ||
arrayDestructureError, | ||
arrayDestructureError, | ||
], | ||
}, | ||
], | ||
}); |