Skip to content

Commit

Permalink
feat(eslint-plugin): new extendedrule 'no-invalid-this'
Browse files Browse the repository at this point in the history
  • Loading branch information
anikethsaha committed Mar 30, 2020
1 parent f3160b4 commit 951faa1
Show file tree
Hide file tree
Showing 7 changed files with 601 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -184,6 +184,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 supports all options and features of the base rule.

## 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 @@ -45,6 +45,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",
"no-magic-numbers": "off",
"@typescript-eslint/no-magic-numbers": "error",
"@typescript-eslint/no-misused-new": "error",
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -38,6 +38,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 noMagicNumbers from './no-magic-numbers';
import noMisusedNew from './no-misused-new';
import noMisusedPromises from './no-misused-promises';
Expand Down Expand Up @@ -133,6 +134,7 @@ export default {
'no-for-in-array': noForInArray,
'no-implied-eval': noImpliedEval,
'no-inferrable-types': noInferrableTypes,
'no-invalid-this': noInvalidThis,
'no-magic-numbers': noMagicNumbers,
'no-misused-new': noMisusedNew,
'no-misused-promises': noMisusedPromises,
Expand Down
86 changes: 86 additions & 0 deletions packages/eslint-plugin/src/rules/no-invalid-this.ts
@@ -0,0 +1,86 @@
import { TSESTree } from '@typescript-eslint/experimental-utils';
import baseRule from 'eslint/lib/rules/no-invalid-this';
import {
InferOptionsTypeFromRule,
InferMessageIdsTypeFromRule,
createRule,
deepMerge,
} from '../util';

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

const schema = deepMerge(
Array.isArray(baseRule.meta.schema)
? baseRule.meta.schema[0]
: baseRule.meta.schema,
{
properties: {
capIsConstructor: {
type: 'boolean',
default: true,
},
},
},
);

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: [schema],
},
defaultOptions: [{ capIsConstructor: true }],
create(context) {
const rules = baseRule.create(context);
let argList: Array<string[]> = [];

return {
...rules,
FunctionDeclaration(node: TSESTree.FunctionDeclaration): void {
const names = node?.params.map(
(param: TSESTree.Parameter) => param?.name,
);
argList.push(names);
// 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 {
const names = node?.params.map(
(param: TSESTree.Parameter) => param?.name,
);
argList.push(names);
// 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) {
const lastFnArg = argList[argList.length - 1];

if (lastFnArg?.some((name: string) => name === 'this')) {
return;
}

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

0 comments on commit 951faa1

Please sign in to comment.