typescript-eslint / typescript-eslint

@@ -1,11 +1,12 @@
Loading
1 1
import { TSESTree } from '@typescript-eslint/experimental-utils';
2 2
import * as ts from 'typescript';
3 +
import * as tsutils from 'tsutils';
3 4
import baseRule from 'eslint/lib/rules/dot-notation';
4 5
import {
5 -
  InferOptionsTypeFromRule,
6 -
  InferMessageIdsTypeFromRule,
7 6
  createRule,
8 7
  getParserServices,
8 +
  InferMessageIdsTypeFromRule,
9 +
  InferOptionsTypeFromRule,
9 10
} from '../util';
10 11
11 12
export type Options = InferOptionsTypeFromRule<typeof baseRule>;
@@ -42,6 +43,10 @@
Loading
42 43
            type: 'boolean',
43 44
            default: false,
44 45
          },
46 +
          allowIndexSignaturePropertyAccess: {
47 +
            type: 'boolean',
48 +
            default: false,
49 +
          },
45 50
        },
46 51
        additionalProperties: false,
47 52
      },
@@ -53,32 +58,41 @@
Loading
53 58
    {
54 59
      allowPrivateClassPropertyAccess: false,
55 60
      allowProtectedClassPropertyAccess: false,
61 +
      allowIndexSignaturePropertyAccess: false,
56 62
      allowKeywords: true,
57 63
      allowPattern: '',
58 64
    },
59 65
  ],
60 66
  create(context, [options]) {
61 67
    const rules = baseRule.create(context);
68 +
69 +
    const { program, esTreeNodeToTSNodeMap } = getParserServices(context);
70 +
    const typeChecker = program.getTypeChecker();
71 +
62 72
    const allowPrivateClassPropertyAccess =
63 73
      options.allowPrivateClassPropertyAccess;
64 74
    const allowProtectedClassPropertyAccess =
65 75
      options.allowProtectedClassPropertyAccess;
66 -
67 -
    const parserServices = getParserServices(context);
68 -
    const typeChecker = parserServices.program.getTypeChecker();
76 +
    const allowIndexSignaturePropertyAccess =
77 +
      (options.allowIndexSignaturePropertyAccess ?? false) ||
78 +
      tsutils.isCompilerOptionEnabled(
79 +
        program.getCompilerOptions(),
80 +
        'noPropertyAccessFromIndexSignature',
81 +
      );
69 82
70 83
    return {
71 84
      MemberExpression(node: TSESTree.MemberExpression): void {
72 85
        if (
73 86
          (allowPrivateClassPropertyAccess ||
74 -
            allowProtectedClassPropertyAccess) &&
87 +
            allowProtectedClassPropertyAccess ||
88 +
            allowIndexSignaturePropertyAccess) &&
75 89
          node.computed
76 90
        ) {
77 -
          // for perf reasons - only fetch the symbol if we have to
78 -
          const objectSymbol = typeChecker.getSymbolAtLocation(
79 -
            parserServices.esTreeNodeToTSNodeMap.get(node.property),
91 +
          // for perf reasons - only fetch symbols if we have to
92 +
          const propertySymbol = typeChecker.getSymbolAtLocation(
93 +
            esTreeNodeToTSNodeMap.get(node.property),
80 94
          );
81 -
          const modifierKind = objectSymbol?.getDeclarations()?.[0]
95 +
          const modifierKind = propertySymbol?.getDeclarations()?.[0]
82 96
            ?.modifiers?.[0].kind;
83 97
          if (
84 98
            (allowPrivateClassPropertyAccess &&
@@ -88,6 +102,21 @@
Loading
88 102
          ) {
89 103
            return;
90 104
          }
105 +
          if (
106 +
            propertySymbol === undefined &&
107 +
            allowIndexSignaturePropertyAccess
108 +
          ) {
109 +
            const objectType = typeChecker.getTypeAtLocation(
110 +
              esTreeNodeToTSNodeMap.get(node.object),
111 +
            );
112 +
            const indexType = typeChecker.getIndexTypeOfType(
113 +
              objectType,
114 +
              ts.IndexKind.String,
115 +
            );
116 +
            if (indexType != undefined) {
117 +
              return;
118 +
            }
119 +
          }
91 120
        }
92 121
        rules.MemberExpression(node);
93 122
      },
Files Coverage
packages 92.68%
Project Totals (318 files) 92.68%
codecov
Build #845689107 -
unittest
1
codecov:
2
  require_ci_to_pass: no
3
  notify:
4
    wait_for_ci: no
5

6
coverage:
7
  status:
8
    patch:
9
      default:
10
        target: 90%
11
    project:
12
      default:
13
        target: auto
14
        threshold: 2%
15

16
comment:
17
  layout: diff, flags, files
18
  require_changes: true
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading