Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): new extended rule 'no-invalid-this' #1823

Merged
merged 11 commits into from May 4, 2020
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,
InferMessageIdsTypeFromRule,
createRule,
} 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: Array<string[]> = [];

return {
...rules,
FunctionDeclaration(node: TSESTree.FunctionDeclaration): void {
argList.push(
node.params.some(
(param: TSESTree.Identifier) =>
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
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: TSESTree.Identifier) =>
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
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.length > 0) {
return;
}

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