diff --git a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts index f98e59b4cd65..260650d198c7 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts @@ -1,6 +1,7 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; +import type * as ts from 'typescript'; import { createRule, @@ -15,6 +16,11 @@ const enum State { Safe = 2, } +function createDataType(type: ts.Type): '`error` typed' | '`any`' { + const isErrorType = tsutils.isIntrinsicErrorType(type); + return isErrorType ? '`error` typed' : '`any`'; +} + export default createRule({ name: 'no-unsafe-member-access', meta: { @@ -26,13 +32,13 @@ export default createRule({ }, messages: { unsafeMemberExpression: - 'Unsafe member access {{property}} on an `any` value.', + 'Unsafe member access {{property}} on an {{type}} value.', unsafeThisMemberExpression: [ 'Unsafe member access {{property}} on an `any` value. `this` is typed as `any`.', 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', ].join('\n'), unsafeComputedMemberAccess: - 'Computed name {{property}} resolves to an any value.', + 'Computed name {{property}} resolves to an {{type}} value.', }, schema: [], }, @@ -92,6 +98,7 @@ export default createRule({ messageId, data: { property: node.computed ? `[${propertyName}]` : `.${propertyName}`, + type: createDataType(type), }, }); } @@ -127,6 +134,7 @@ export default createRule({ messageId: 'unsafeComputedMemberAccess', data: { property: `[${propertyName}]`, + type: createDataType(type), }, }); } diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-member-access.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-member-access.shot index 33b46a7e5921..ac2342c31da5 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-member-access.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-member-access.shot @@ -27,9 +27,9 @@ nestedAny.prop[key]; // Using an any to access a member is unsafe const arr = [1, 2, 3]; arr[anyVar]; - ~~~~~~ Computed name [anyVar] resolves to an any value. + ~~~~~~ Computed name [anyVar] resolves to an \`any\` value. nestedAny[anyVar]; - ~~~~~~ Computed name [anyVar] resolves to an any value. + ~~~~~~ Computed name [anyVar] resolves to an \`any\` value. " `; diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts index 292c52759c0d..a35d14c1e0c8 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts @@ -75,6 +75,7 @@ function foo(x: any) { column: 5, endColumn: 6, data: { + type: '`any`', property: '.a', }, }, @@ -93,6 +94,7 @@ function foo(x: any) { column: 5, endColumn: 6, data: { + type: '`any`', property: '.a', }, }, @@ -111,6 +113,7 @@ function foo(x: { a: any }) { column: 7, endColumn: 8, data: { + type: '`any`', property: '.b', }, }, @@ -129,6 +132,7 @@ function foo(x: any) { column: 5, endColumn: 8, data: { + type: '`any`', property: "['a']", }, }, @@ -147,6 +151,7 @@ function foo(x: any) { column: 5, endColumn: 8, data: { + type: '`any`', property: "['a']", }, }, @@ -154,6 +159,25 @@ function foo(x: any) { }, { code: ` +let value: NotKnown; + +value.property; + `, + errors: [ + { + messageId: 'unsafeMemberExpression', + line: 4, + column: 7, + endColumn: 15, + data: { + type: '`error` typed', + property: '.property', + }, + }, + ], + }, + { + code: ` function foo(x: { a: number }, y: any) { x[y]; } @@ -166,6 +190,7 @@ function foo(x: { a: number }, y: any) { endColumn: 6, data: { property: '[y]', + type: '`any`', }, }, ], @@ -184,6 +209,7 @@ function foo(x?: { a: number }, y: any) { endColumn: 8, data: { property: '[y]', + type: '`any`', }, }, ], @@ -202,6 +228,7 @@ function foo(x: { a: number }, y: any) { endColumn: 12, data: { property: '[y += 1]', + type: '`any`', }, }, ], @@ -220,6 +247,7 @@ function foo(x: { a: number }, y: any) { endColumn: 13, data: { property: '[1 as any]', + type: '`any`', }, }, ], @@ -238,6 +266,7 @@ function foo(x: { a: number }, y: any) { endColumn: 8, data: { property: '[y()]', + type: '`any`', }, }, ], @@ -256,6 +285,26 @@ function foo(x: string[], y: any) { endColumn: 6, data: { property: '[y]', + type: '`any`', + }, + }, + ], + }, + { + code: ` +function foo(x: { a: number }, y: NotKnown) { + x[y]; +} + `, + errors: [ + { + messageId: 'unsafeComputedMemberAccess', + line: 3, + column: 5, + endColumn: 6, + data: { + property: '[y]', + type: '`error` typed', }, }, ],