diff --git a/packages/eslint-plugin/docs/rules/dot-notation.md b/packages/eslint-plugin/docs/rules/dot-notation.md
index 56097090e2e..094e2e4fc67 100644
--- a/packages/eslint-plugin/docs/rules/dot-notation.md
+++ b/packages/eslint-plugin/docs/rules/dot-notation.md
@@ -3,7 +3,10 @@
## Rule Details
This rule extends the base [`eslint/dot-notation`](https://eslint.org/docs/rules/dot-notation) rule.
-It adds support for optionally ignoring computed `private` member access.
+It adds:
+
+- Support for optionally ignoring computed `private` and/or `protected` member access.
+- Compatibility with TypeScript's `noPropertyAccessFromIndexSignature` option.
## How to use
@@ -24,14 +27,18 @@ This rule adds the following options:
interface Options extends BaseDotNotationOptions {
allowPrivateClassPropertyAccess?: boolean;
allowProtectedClassPropertyAccess?: boolean;
+ allowIndexSignaturePropertyAccess?: boolean;
}
const defaultOptions: Options = {
...baseDotNotationDefaultOptions,
allowPrivateClassPropertyAccess: false,
allowProtectedClassPropertyAccess: false,
+ allowIndexSignaturePropertyAccess: false,
};
```
+If the TypeScript compiler option `noPropertyAccessFromIndexSignature` is set to `true`, then this rule always allows the use of square bracket notation to access properties of types that have a `string` index signature, even if `allowIndexSignaturePropertyAccess` is `false`.
+
### `allowPrivateClassPropertyAccess`
Example of a correct code when `allowPrivateClassPropertyAccess` is set to `true`
@@ -58,4 +65,19 @@ const x = new X();
x['protected_prop'] = 123;
```
+### `allowIndexSignaturePropertyAccess`
+
+Example of correct code when `allowIndexSignaturePropertyAccess` is set to `true`
+
+```ts
+class X {
+ [key: string]: number;
+}
+
+const x = new X();
+x['hello'] = 123;
+```
+
+If the TypeScript compiler option `noPropertyAccessFromIndexSignature` is set to `true`, then the above code is always allowed, even if `allowIndexSignaturePropertyAccess` is `false`.
+
Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/dot-notation.md)
diff --git a/packages/eslint-plugin/src/rules/dot-notation.ts b/packages/eslint-plugin/src/rules/dot-notation.ts
index 2c9dd9f2351..055b97ecab2 100644
--- a/packages/eslint-plugin/src/rules/dot-notation.ts
+++ b/packages/eslint-plugin/src/rules/dot-notation.ts
@@ -1,11 +1,12 @@
import { TSESTree } from '@typescript-eslint/experimental-utils';
import * as ts from 'typescript';
+import * as tsutils from 'tsutils';
import baseRule from 'eslint/lib/rules/dot-notation';
import {
- InferOptionsTypeFromRule,
- InferMessageIdsTypeFromRule,
createRule,
getParserServices,
+ InferMessageIdsTypeFromRule,
+ InferOptionsTypeFromRule,
} from '../util';
export type Options = InferOptionsTypeFromRule;
@@ -42,6 +43,10 @@ export default createRule({
type: 'boolean',
default: false,
},
+ allowIndexSignaturePropertyAccess: {
+ type: 'boolean',
+ default: false,
+ },
},
additionalProperties: false,
},
@@ -53,32 +58,41 @@ export default createRule({
{
allowPrivateClassPropertyAccess: false,
allowProtectedClassPropertyAccess: false,
+ allowIndexSignaturePropertyAccess: false,
allowKeywords: true,
allowPattern: '',
},
],
create(context, [options]) {
const rules = baseRule.create(context);
+
+ const { program, esTreeNodeToTSNodeMap } = getParserServices(context);
+ const typeChecker = program.getTypeChecker();
+
const allowPrivateClassPropertyAccess =
options.allowPrivateClassPropertyAccess;
const allowProtectedClassPropertyAccess =
options.allowProtectedClassPropertyAccess;
-
- const parserServices = getParserServices(context);
- const typeChecker = parserServices.program.getTypeChecker();
+ const allowIndexSignaturePropertyAccess =
+ (options.allowIndexSignaturePropertyAccess ?? false) ||
+ tsutils.isCompilerOptionEnabled(
+ program.getCompilerOptions(),
+ 'noPropertyAccessFromIndexSignature',
+ );
return {
MemberExpression(node: TSESTree.MemberExpression): void {
if (
(allowPrivateClassPropertyAccess ||
- allowProtectedClassPropertyAccess) &&
+ allowProtectedClassPropertyAccess ||
+ allowIndexSignaturePropertyAccess) &&
node.computed
) {
- // for perf reasons - only fetch the symbol if we have to
- const objectSymbol = typeChecker.getSymbolAtLocation(
- parserServices.esTreeNodeToTSNodeMap.get(node.property),
+ // for perf reasons - only fetch symbols if we have to
+ const propertySymbol = typeChecker.getSymbolAtLocation(
+ esTreeNodeToTSNodeMap.get(node.property),
);
- const modifierKind = objectSymbol?.getDeclarations()?.[0]
+ const modifierKind = propertySymbol?.getDeclarations()?.[0]
?.modifiers?.[0].kind;
if (
(allowPrivateClassPropertyAccess &&
@@ -88,6 +102,21 @@ export default createRule({
) {
return;
}
+ if (
+ propertySymbol === undefined &&
+ allowIndexSignaturePropertyAccess
+ ) {
+ const objectType = typeChecker.getTypeAtLocation(
+ esTreeNodeToTSNodeMap.get(node.object),
+ );
+ const indexType = typeChecker.getIndexTypeOfType(
+ objectType,
+ ts.IndexKind.String,
+ );
+ if (indexType != undefined) {
+ return;
+ }
+ }
}
rules.MemberExpression(node);
},
diff --git a/packages/eslint-plugin/tests/rules/dot-notation.test.ts b/packages/eslint-plugin/tests/rules/dot-notation.test.ts
index 0d1f755b482..cbdb2d52342 100644
--- a/packages/eslint-plugin/tests/rules/dot-notation.test.ts
+++ b/packages/eslint-plugin/tests/rules/dot-notation.test.ts
@@ -87,6 +87,18 @@ x['protected_prop'] = 123;
`,
options: [{ allowProtectedClassPropertyAccess: true }],
},
+ {
+ code: `
+class X {
+ prop: string;
+ [key: string]: number;
+}
+
+const x = new X();
+x['hello'] = 3;
+ `,
+ options: [{ allowIndexSignaturePropertyAccess: true }],
+ },
],
invalid: [
{
@@ -287,5 +299,27 @@ x.protected_prop = 123;
`,
errors: [{ messageId: 'useDot' }],
},
+ {
+ code: `
+class X {
+ prop: string;
+ [key: string]: number;
+}
+
+const x = new X();
+x['prop'] = 'hello';
+ `,
+ options: [{ allowIndexSignaturePropertyAccess: true }],
+ errors: [{ messageId: 'useDot' }],
+ output: `
+class X {
+ prop: string;
+ [key: string]: number;
+}
+
+const x = new X();
+x.prop = 'hello';
+ `,
+ },
],
});
diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts
index 425956e1f25..d6677e9dc93 100644
--- a/packages/eslint-plugin/typings/eslint-rules.d.ts
+++ b/packages/eslint-plugin/typings/eslint-rules.d.ts
@@ -713,6 +713,7 @@ declare module 'eslint/lib/rules/dot-notation' {
allowPattern?: string;
allowPrivateClassPropertyAccess?: boolean;
allowProtectedClassPropertyAccess?: boolean;
+ allowIndexSignaturePropertyAccess?: boolean;
},
],
{