Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(eslint-plugin): add rule no-unsafe-call
- Loading branch information
1 parent
a633ca2
commit cbf1242
Showing
6 changed files
with
163 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,36 @@ | ||
# Disallows calling an any type value (`no-unsafe-call`) | ||
|
||
Despite your best intentions, the `any` type can sometimes leak into your codebase. | ||
Member access on `any` typed variables is not checked at all by TypeScript, so it creates a potential safety hole, and source of bugs in your codebase. | ||
|
||
## Rule Details | ||
|
||
This rule disallows calling any variable that is typed as `any`. | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```ts | ||
declare const anyVar: any; | ||
declare const nestedAny: { prop: any }; | ||
|
||
anyVar(); | ||
anyVar.a.b(); | ||
|
||
nestedAny.prop(); | ||
nestedAny.prop['a'](); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```ts | ||
declare const properlyTyped: { prop: { a: () => void } }; | ||
|
||
nestedAny.prop.a(); | ||
|
||
(() => {})(); | ||
``` | ||
|
||
## Related to | ||
|
||
- [`no-explicit-any`](./no-explicit-any.md) | ||
- TSLint: [`no-unsafe-any`](https://palantir.github.io/tslint/rules/no-unsafe-any/) |
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
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,38 @@ | ||
import * as util from '../util'; | ||
import { TSESTree } from '@typescript-eslint/experimental-utils'; | ||
|
||
export default util.createRule({ | ||
name: 'no-unsafe-call', | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: 'Disallows calling an any type value', | ||
category: 'Possible Errors', | ||
recommended: false, | ||
requiresTypeChecking: true, | ||
}, | ||
messages: { | ||
unsafeCall: 'Unsafe call of an any typed value', | ||
}, | ||
schema: [], | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); | ||
const checker = program.getTypeChecker(); | ||
|
||
return { | ||
'CallExpression, OptionalCallExpression'( | ||
node: TSESTree.CallExpression | TSESTree.OptionalCallExpression, | ||
): void { | ||
const tsNode = esTreeNodeToTSNodeMap.get(node.callee); | ||
if (util.isAnyType(tsNode, checker)) { | ||
context.report({ | ||
node: node.callee, | ||
messageId: 'unsafeCall', | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}); |
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,85 @@ | ||
import rule from '../../src/rules/no-unsafe-call'; | ||
import { | ||
RuleTester, | ||
batchedSingleLineTests, | ||
getFixturesRootDir, | ||
} from '../RuleTester'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
project: './tsconfig.json', | ||
tsconfigRootDir: getFixturesRootDir(), | ||
}, | ||
}); | ||
|
||
ruleTester.run('no-unsafe-call', rule, { | ||
valid: [ | ||
'function foo(x: () => void) { x() }', | ||
'function foo(x?: { a: () => void }) { x?.a() }', | ||
'function foo(x: { a?: () => void }) { x.a?.() }', | ||
], | ||
invalid: [ | ||
...batchedSingleLineTests({ | ||
code: ` | ||
function foo(x: any) { x() } | ||
function foo(x: any) { x?.() } | ||
function foo(x: any) { x.a.b.c.d.e.f.g() } | ||
function foo(x: any) { x.a.b.c.d.e.f.g?.() } | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'unsafeCall', | ||
line: 2, | ||
column: 24, | ||
endColumn: 25, | ||
}, | ||
{ | ||
messageId: 'unsafeCall', | ||
line: 3, | ||
column: 24, | ||
endColumn: 25, | ||
}, | ||
{ | ||
messageId: 'unsafeCall', | ||
line: 4, | ||
column: 24, | ||
endColumn: 39, | ||
}, | ||
{ | ||
messageId: 'unsafeCall', | ||
line: 5, | ||
column: 24, | ||
endColumn: 39, | ||
}, | ||
], | ||
}), | ||
...batchedSingleLineTests({ | ||
code: ` | ||
function foo(x: { a: any }) { x.a() } | ||
function foo(x: { a: any }) { x?.a() } | ||
function foo(x: { a: any }) { x.a?.() } | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'unsafeCall', | ||
line: 2, | ||
column: 31, | ||
endColumn: 34, | ||
}, | ||
{ | ||
messageId: 'unsafeCall', | ||
line: 3, | ||
column: 31, | ||
endColumn: 35, | ||
}, | ||
{ | ||
messageId: 'unsafeCall', | ||
line: 4, | ||
column: 31, | ||
endColumn: 34, | ||
}, | ||
], | ||
}), | ||
], | ||
}); |