From 33eb0a785536caab5a872b719f6ec9066dff4e9b Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Mon, 13 Jan 2025 23:35:08 +0900 Subject: [PATCH 1/5] fix(eslint-plugin): [dot-notation] handle noPropertyAccessFromIndexSignature true --- .../eslint-plugin/src/rules/dot-notation.ts | 21 +++++++++++++------ ...ig.noPropertyAccessFromIndexSignature.json | 6 ++++++ .../tests/rules/dot-notation.test.ts | 16 ++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 packages/eslint-plugin/tests/fixtures/tsconfig.noPropertyAccessFromIndexSignature.json diff --git a/packages/eslint-plugin/src/rules/dot-notation.ts b/packages/eslint-plugin/src/rules/dot-notation.ts index dc57cec949fc..a3767f6956c6 100644 --- a/packages/eslint-plugin/src/rules/dot-notation.ts +++ b/packages/eslint-plugin/src/rules/dot-notation.ts @@ -9,7 +9,12 @@ import type { InferOptionsTypeFromRule, } from '../util'; -import { createRule, getModifiers, getParserServices } from '../util'; +import { + createRule, + getModifiers, + getParserServices, + getTypeName, +} from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; const baseRule = getESLintCoreRule('dot-notation'); @@ -82,7 +87,7 @@ export default createRule({ create(context, [options]) { const rules = baseRule.create(context); const services = getParserServices(context); - + const checker = services.program.getTypeChecker(); const allowPrivateClassPropertyAccess = options.allowPrivateClassPropertyAccess; const allowProtectedClassPropertyAccess = @@ -125,12 +130,16 @@ export default createRule({ ) { return; } + if (propertySymbol == null && allowIndexSignaturePropertyAccess) { const objectType = services.getTypeAtLocation(node.object); - const indexType = objectType - .getNonNullableType() - .getStringIndexType(); - if (indexType != null) { + const indexType = objectType.getNonNullableType(); + const indexInfos = checker.getIndexInfosOfType(indexType); + if ( + indexInfos.some( + info => getTypeName(checker, info.keyType) === 'string', + ) + ) { return; } } diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig.noPropertyAccessFromIndexSignature.json b/packages/eslint-plugin/tests/fixtures/tsconfig.noPropertyAccessFromIndexSignature.json new file mode 100644 index 000000000000..c3b3b86747f6 --- /dev/null +++ b/packages/eslint-plugin/tests/fixtures/tsconfig.noPropertyAccessFromIndexSignature.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noPropertyAccessFromIndexSignature": true + } +} diff --git a/packages/eslint-plugin/tests/rules/dot-notation.test.ts b/packages/eslint-plugin/tests/rules/dot-notation.test.ts index 9355a46750af..1827051c99dc 100644 --- a/packages/eslint-plugin/tests/rules/dot-notation.test.ts +++ b/packages/eslint-plugin/tests/rules/dot-notation.test.ts @@ -150,6 +150,22 @@ console.log(x?.['priv_prop']); `, options: [{ allowProtectedClassPropertyAccess: true }], }, + { + code: ` +type Foo = { + bar: boolean; + [key: \`key_\${string}\`]: number; +}; +declare const foo: Foo; +foo['key_baz']; + `, + languageOptions: { + parserOptions: { + project: './tsconfig.noPropertyAccessFromIndexSignature.json', + tsconfigRootDir: rootPath, + }, + }, + }, ], invalid: [ { From 39e76a9f5cd5cf6ffd56ce17ff8896422b4f7651 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Mon, 13 Jan 2025 23:44:12 +0900 Subject: [PATCH 2/5] add test cases --- .../eslint-plugin/src/rules/dot-notation.ts | 1 - .../tests/rules/dot-notation.test.ts | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/dot-notation.ts b/packages/eslint-plugin/src/rules/dot-notation.ts index a3767f6956c6..e48ac724f8af 100644 --- a/packages/eslint-plugin/src/rules/dot-notation.ts +++ b/packages/eslint-plugin/src/rules/dot-notation.ts @@ -130,7 +130,6 @@ export default createRule({ ) { return; } - if (propertySymbol == null && allowIndexSignaturePropertyAccess) { const objectType = services.getTypeAtLocation(node.object); const indexType = objectType.getNonNullableType(); diff --git a/packages/eslint-plugin/tests/rules/dot-notation.test.ts b/packages/eslint-plugin/tests/rules/dot-notation.test.ts index 1827051c99dc..302796b5cd25 100644 --- a/packages/eslint-plugin/tests/rules/dot-notation.test.ts +++ b/packages/eslint-plugin/tests/rules/dot-notation.test.ts @@ -162,6 +162,25 @@ foo['key_baz']; languageOptions: { parserOptions: { project: './tsconfig.noPropertyAccessFromIndexSignature.json', + projectService: false, + tsconfigRootDir: rootPath, + }, + }, + }, + { + code: ` +type Key = Lowercase; +type Foo = { + BAR: boolean; + [key: Lowercase]: number; +}; +declare const foo: Foo; +foo['bar']; + `, + languageOptions: { + parserOptions: { + project: './tsconfig.noPropertyAccessFromIndexSignature.json', + projectService: false, tsconfigRootDir: rootPath, }, }, From 116d4c7d3fb6f2466159294b8b0d5c3080dc75e0 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Tue, 14 Jan 2025 22:34:24 +0900 Subject: [PATCH 3/5] add test cases --- .../tests/rules/dot-notation.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/dot-notation.test.ts b/packages/eslint-plugin/tests/rules/dot-notation.test.ts index 302796b5cd25..78e56567cd6f 100644 --- a/packages/eslint-plugin/tests/rules/dot-notation.test.ts +++ b/packages/eslint-plugin/tests/rules/dot-notation.test.ts @@ -419,5 +419,22 @@ const x = new X(); x.prop = 'hello'; `, }, + { + code: ` +type Foo = { + bar: boolean; + [key: \`key_\${string}\`]: number; +}; +foo['key_baz']; + `, + errors: [{ messageId: 'useDot' }], + output: ` +type Foo = { + bar: boolean; + [key: \`key_\${string}\`]: number; +}; +foo.key_baz; + `, + }, ], }); From 41025ca0b1e7cab9cd0b296ffe3149e80cb7e5e8 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Mon, 20 Jan 2025 20:49:18 +0900 Subject: [PATCH 4/5] apply reviews --- packages/eslint-plugin/src/rules/dot-notation.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/eslint-plugin/src/rules/dot-notation.ts b/packages/eslint-plugin/src/rules/dot-notation.ts index e48ac724f8af..69d53170c70e 100644 --- a/packages/eslint-plugin/src/rules/dot-notation.ts +++ b/packages/eslint-plugin/src/rules/dot-notation.ts @@ -9,12 +9,7 @@ import type { InferOptionsTypeFromRule, } from '../util'; -import { - createRule, - getModifiers, - getParserServices, - getTypeName, -} from '../util'; +import { createRule, getModifiers, getParserServices } from '../util'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; const baseRule = getESLintCoreRule('dot-notation'); @@ -131,12 +126,13 @@ export default createRule({ return; } if (propertySymbol == null && allowIndexSignaturePropertyAccess) { - const objectType = services.getTypeAtLocation(node.object); - const indexType = objectType.getNonNullableType(); - const indexInfos = checker.getIndexInfosOfType(indexType); + const objectType = services + .getTypeAtLocation(node.object) + .getNonNullableType(); + const indexInfos = checker.getIndexInfosOfType(objectType); if ( indexInfos.some( - info => getTypeName(checker, info.keyType) === 'string', + info => info.keyType.flags & ts.TypeFlags.StringLike, ) ) { return; From 2894e85a75d489c8ac6bffda7c8a932f4eec4ac0 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Mon, 3 Feb 2025 09:21:07 +0900 Subject: [PATCH 5/5] Add testcases --- .../tests/rules/dot-notation.test.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/dot-notation.test.ts b/packages/eslint-plugin/tests/rules/dot-notation.test.ts index 78e56567cd6f..0a14e1036383 100644 --- a/packages/eslint-plugin/tests/rules/dot-notation.test.ts +++ b/packages/eslint-plugin/tests/rules/dot-notation.test.ts @@ -185,6 +185,27 @@ foo['bar']; }, }, }, + { + code: ` +type ExtraKey = \`extra\${string}\`; + +type Foo = { + foo: string; + [extraKey: ExtraKey]: number; +}; + +function f(x: T) { + x['extraKey']; +} + `, + languageOptions: { + parserOptions: { + project: './tsconfig.noPropertyAccessFromIndexSignature.json', + projectService: false, + tsconfigRootDir: rootPath, + }, + }, + }, ], invalid: [ { @@ -436,5 +457,32 @@ type Foo = { foo.key_baz; `, }, + { + code: ` +type ExtraKey = \`extra\${string}\`; + +type Foo = { + foo: string; + [extraKey: ExtraKey]: number; +}; + +function f(x: T) { + x['extraKey']; +} + `, + errors: [{ messageId: 'useDot' }], + output: ` +type ExtraKey = \`extra\${string}\`; + +type Foo = { + foo: string; + [extraKey: ExtraKey]: number; +}; + +function f(x: T) { + x.extraKey; +} + `, + }, ], });