Skip to content

Commit

Permalink
feat(eslint-plugin): [no-unsafe-member-access] report any typed… (#1683)
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed Mar 9, 2020
1 parent 55a58ff commit 1543117
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 1 deletion.
11 changes: 11 additions & 0 deletions packages/eslint-plugin/docs/rules/no-unsafe-member-access.md
Expand Up @@ -23,6 +23,11 @@ nestedAny.prop['a'];

const key = 'a';
nestedAny.prop[key];

// usng an any to access a member is unsafe
const arr = [1, 2, 3];
arr[anyVar];
nestedAny[anyVar];
```

Examples of **correct** code for this rule:
Expand All @@ -35,6 +40,12 @@ nestedAny.prop['a'];

const key = 'a';
nestedAny.prop[key];

const arr = [1, 2, 3];
arr[1];
const idx = 1;
arr[idx];
arr[idx++];
```

## Related to
Expand Down
36 changes: 35 additions & 1 deletion packages/eslint-plugin/src/rules/no-unsafe-member-access.ts
@@ -1,4 +1,7 @@
import { TSESTree } from '@typescript-eslint/experimental-utils';
import {
TSESTree,
AST_NODE_TYPES,
} from '@typescript-eslint/experimental-utils';
import * as util from '../util';

const enum State {
Expand All @@ -19,6 +22,8 @@ export default util.createRule({
messages: {
unsafeMemberExpression:
'Unsafe member access {{property}} on an any value',
unsafeComputedMemberAccess:
'Computed name {{property}} resolves to an any value',
},
schema: [],
},
Expand Down Expand Up @@ -69,6 +74,35 @@ export default util.createRule({

return {
'MemberExpression, OptionalMemberExpression': checkMemberExpression,
':matches(MemberExpression, OptionalMemberExpression)[computed = true] > *.property'(
node: TSESTree.Expression,
): void {
if (
// x[1]
node.type === AST_NODE_TYPES.Literal ||
// x[1++] x[++x] etc
// FUN FACT - **all** update expressions return type number, regardless of the argument's type,
// because JS engines return NaN if there the argument is not a number.
node.type === AST_NODE_TYPES.UpdateExpression
) {
// perf optimizations - literals can obviously never be `any`
return;
}

const tsNode = esTreeNodeToTSNodeMap.get(node);
const type = checker.getTypeAtLocation(tsNode);

if (util.isTypeAnyType(type)) {
const propertyName = sourceCode.getText(node);
context.report({
node,
messageId: 'unsafeComputedMemberAccess',
data: {
property: `[${propertyName}]`,
},
});
}
},
};
},
});
74 changes: 74 additions & 0 deletions packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts
Expand Up @@ -15,8 +15,16 @@ const ruleTester = new RuleTester({

ruleTester.run('no-unsafe-member-access', rule, {
valid: [
'function foo(x: { a: number }, y: any) { x[y++] }',
'function foo(x: { a: number }) { x.a }',
'function foo(x?: { a: number }) { x?.a }',
'function foo(x: { a: number }) { x["a"] }',
'function foo(x?: { a: number }) { x?.["a"] }',
'function foo(x: { a: number }, y: string) { x[y] }',
'function foo(x?: { a: number }, y: string) { x?.[y] }',
'function foo(x: string[]) { x[1] }',
'function foo(x?: string[]) { x?.[1++] }',
'function foo(x?: string[]) { x?.[(1 as any)++] }',
],
invalid: [
...batchedSingleLineTests({
Expand Down Expand Up @@ -81,5 +89,71 @@ function foo(x: any) { x['a']['b']['c'] }
},
],
}),
...batchedSingleLineTests({
code: `
function foo(x: { a: number }, y: any) { x[y] }
function foo(x?: { a: number }, y: any) { x?.[y] }
function foo(x: { a: number }, y: any) { x[y += 1] }
function foo(x: { a: number }, y: any) { x[1 as any] }
function foo(x: { a: number }, y: any) { x[y()] }
function foo(x: string[], y: any) { x[y] }
`,
errors: [
{
messageId: 'unsafeComputedMemberAccess',
data: {
property: '[y]',
},
line: 2,
column: 44,
endColumn: 45,
},
{
messageId: 'unsafeComputedMemberAccess',
data: {
property: '[y]',
},
line: 3,
column: 47,
endColumn: 48,
},
{
messageId: 'unsafeComputedMemberAccess',
data: {
property: '[y += 1]',
},
line: 4,
column: 44,
endColumn: 50,
},
{
messageId: 'unsafeComputedMemberAccess',
data: {
property: '[1 as any]',
},
line: 5,
column: 44,
endColumn: 52,
},
{
messageId: 'unsafeComputedMemberAccess',
data: {
property: '[y()]',
},
line: 6,
column: 44,
endColumn: 47,
},
{
messageId: 'unsafeComputedMemberAccess',
data: {
property: '[y]',
},
line: 7,
column: 39,
endColumn: 40,
},
],
}),
],
});

0 comments on commit 1543117

Please sign in to comment.