Skip to content

Commit

Permalink
feat(eslint-plugin): new extended rule 'no-invalid-this' (#1823)
Browse files Browse the repository at this point in the history
Co-Authored-By: Brad Zacher <brad.zacher@gmail.com>
  • Loading branch information
anikethsaha and bradzacher committed May 4, 2020
1 parent 2f0824b commit b18bc35
Show file tree
Hide file tree
Showing 7 changed files with 1,030 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -192,6 +192,7 @@ In these cases, we create what we call an extension rule; a rule within our plug
| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :heavy_check_mark: | | |
| [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | |
| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | | :wrench: | |
| [`@typescript-eslint/no-invalid-this`](./docs/rules/no-invalid-this.md) | disallow `this` keywords outside of classes or class-like objects | | | |
| [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallow magic numbers | | | |
| [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | |
| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | |
Expand Down
26 changes: 26 additions & 0 deletions packages/eslint-plugin/docs/rules/no-invalid-this.md
@@ -0,0 +1,26 @@
# disallow `this` keywords outside of classes or class-like objects (`no-invalid-this`)

## Rule Details

This rule extends the base [`eslint/no-invalid-this`](https://eslint.org/docs/rules/no-invalid-this) rule.
It adds support for TypeScript's `this` parameters.

## How to use

```cjson
{
// note you must disable the base rule as it can report incorrect errors
"no-invalid-this": "off",
"@typescript-eslint/no-invalid-this": ["error"]
}
```

## Options

See [`eslint/no-invalid-this` options](https://eslint.org/docs/rules/no-invalid-this#options).

## When Not To Use It

When you are indifferent as to how your variables are initialized.

<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-invalid-this.md)</sup>
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/configs/all.json
Expand Up @@ -52,6 +52,8 @@
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-implied-eval": "error",
"@typescript-eslint/no-inferrable-types": "error",
"no-invalid-this": "off",
"@typescript-eslint/no-invalid-this": "error",
"@typescript-eslint/no-invalid-void-type": "error",
"no-magic-numbers": "off",
"@typescript-eslint/no-magic-numbers": "error",
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -41,6 +41,7 @@ import noFloatingPromises from './no-floating-promises';
import noForInArray from './no-for-in-array';
import noImpliedEval from './no-implied-eval';
import noInferrableTypes from './no-inferrable-types';
import noInvalidThis from './no-invalid-this';
import noInvalidVoidType from './no-invalid-void-type';
import noMagicNumbers from './no-magic-numbers';
import noMisusedNew from './no-misused-new';
Expand Down Expand Up @@ -145,6 +146,7 @@ export default {
'no-for-in-array': noForInArray,
'no-implied-eval': noImpliedEval,
'no-inferrable-types': noInferrableTypes,
'no-invalid-this': noInvalidThis,
'no-invalid-void-type': noInvalidVoidType,
'no-magic-numbers': noMagicNumbers,
'no-misused-new': noMisusedNew,
Expand Down
78 changes: 78 additions & 0 deletions packages/eslint-plugin/src/rules/no-invalid-this.ts
@@ -0,0 +1,78 @@
import {
TSESTree,
AST_NODE_TYPES,
} from '@typescript-eslint/experimental-utils';
import baseRule from 'eslint/lib/rules/no-invalid-this';
import {
InferOptionsTypeFromRule,
createRule,
InferMessageIdsTypeFromRule,
} from '../util';

export type Options = InferOptionsTypeFromRule<typeof baseRule>;
export type MessageIds = InferMessageIdsTypeFromRule<typeof baseRule>;

export default createRule<Options, MessageIds>({
name: 'no-invalid-this',
meta: {
type: 'suggestion',
docs: {
description:
'disallow `this` keywords outside of classes or class-like objects',
category: 'Best Practices',
recommended: false,
extendsBaseRule: true,
},
messages: baseRule.meta.messages,
schema: baseRule.meta.schema,
},
defaultOptions: [{ capIsConstructor: true }],
create(context) {
const rules = baseRule.create(context);
const argList: boolean[] = [];

return {
...rules,
FunctionDeclaration(node: TSESTree.FunctionDeclaration): void {
argList.push(
node.params.some(
param =>
param.type === AST_NODE_TYPES.Identifier && param.name === 'this',
),
);
// baseRule's work
rules.FunctionDeclaration(node);
},
'FunctionDeclaration:exit'(node: TSESTree.FunctionDeclaration): void {
argList.pop();
// baseRule's work
rules['FunctionDeclaration:exit'](node);
},
FunctionExpression(node: TSESTree.FunctionExpression): void {
argList.push(
node.params.some(
param =>
param.type === AST_NODE_TYPES.Identifier && param.name === 'this',
),
);
// baseRule's work
rules.FunctionExpression(node);
},
'FunctionExpression:exit'(node: TSESTree.FunctionExpression): void {
argList.pop();
// baseRule's work
rules['FunctionExpression:exit'](node);
},
ThisExpression(node: TSESTree.ThisExpression): void {
const lastFnArg = argList[argList.length - 1];

if (lastFnArg) {
return;
}

// baseRule's work
rules.ThisExpression(node);
},
};
},
});

0 comments on commit b18bc35

Please sign in to comment.