diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index d806f4c59bfa..d48bc0d3d428 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -4,10 +4,13 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; +import type { NodeWithKey } from '../util'; + import { createRule, getFunctionHeadLoc, getParserServices, + getStaticMemberAccessValue, isArrayMethodCallWithPredicate, isFunction, isRestParameterDeclaration, @@ -470,9 +473,6 @@ export default createRule({ }); } } else if (ts.isMethodDeclaration(tsNode)) { - if (ts.isComputedPropertyName(tsNode.name)) { - return; - } const obj = tsNode.parent; // Below condition isn't satisfied unless something goes wrong, @@ -492,9 +492,14 @@ export default createRule({ if (objType == null) { return; } - const propertySymbol = checker.getPropertyOfType( + const staticAccessValue = getStaticMemberAccessValue(node, context); + if (staticAccessValue == null) { + return; + } + const propertySymbol = getMemberIfExists( objType, - tsNode.name.text, + staticAccessValue, + checker, ); if (propertySymbol == null) { return; @@ -594,11 +599,20 @@ export default createRule({ continue; } + const staticAccessValue = getStaticMemberAccessValue( + node as NodeWithKey, + context, + ); + + if (staticAccessValue == null) { + continue; + } + for (const heritageType of heritageTypes) { checkHeritageTypeForMemberReturningVoid( nodeMember, heritageType, - memberName, + staticAccessValue, ); } } @@ -614,9 +628,13 @@ export default createRule({ function checkHeritageTypeForMemberReturningVoid( nodeMember: ts.Node, heritageType: ts.Type, - memberName: string, + staticAccessValue: string | symbol, ): void { - const heritageMember = getMemberIfExists(heritageType, memberName); + const heritageMember = getMemberIfExists( + heritageType, + staticAccessValue, + checker, + ); if (heritageMember == null) { return; } @@ -970,18 +988,45 @@ function getHeritageTypes( .map(typeExpression => checker.getTypeAtLocation(typeExpression)); } +function getWellKnownStringOfSymbol(symbol: symbol): string | null { + const globalSymbolKeys = Object.getOwnPropertyNames(Symbol); + + for (const key of globalSymbolKeys) { + if (symbol === Symbol[key as keyof typeof Symbol]) { + return key; + } + } + + return null; +} + /** - * @returns The member with the given name in `type`, if it exists. + * @returns The member with the given name or known-symbol in `type`, if it exists. */ function getMemberIfExists( type: ts.Type, - memberName: string, + staticAccessValue: string | symbol, + checker: ts.TypeChecker, ): ts.Symbol | undefined { - const escapedMemberName = ts.escapeLeadingUnderscores(memberName); - const symbolMemberMatch = type.getSymbol()?.members?.get(escapedMemberName); - return ( - symbolMemberMatch ?? tsutils.getPropertyOfType(type, escapedMemberName) - ); + if (typeof staticAccessValue === 'string') { + const escapedMemberName = ts.escapeLeadingUnderscores(staticAccessValue); + const symbolMemberMatch = type.getSymbol()?.members?.get(escapedMemberName); + return ( + symbolMemberMatch ?? tsutils.getPropertyOfType(type, escapedMemberName) + ); + } + + const wellKnownSymbolName = getWellKnownStringOfSymbol(staticAccessValue); + + if (wellKnownSymbolName != null) { + return tsutils.getWellKnownSymbolPropertyOfType( + type, + wellKnownSymbolName, + checker, + ); + } + + return undefined; } function isStaticMember(node: TSESTree.Node): boolean { diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index c75a000487f7..d67bbf0ea90c 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -234,13 +234,15 @@ function isParenlessArrowFunction( ); } -type NodeWithKey = +export type NodeWithKey = | TSESTree.MemberExpression | TSESTree.MethodDefinition | TSESTree.Property | TSESTree.PropertyDefinition | TSESTree.TSAbstractMethodDefinition - | TSESTree.TSAbstractPropertyDefinition; + | TSESTree.TSAbstractPropertyDefinition + | TSESTree.TSMethodSignature + | TSESTree.TSPropertySignature; /** * Gets a member being accessed or declared if its value can be determined statically, and diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index 16ee46b5f01d..56824c676ab7 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -1,4 +1,4 @@ -import { RuleTester } from '@typescript-eslint/rule-tester'; +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import rule from '../../src/rules/no-misused-promises'; import { getFixturesRootDir } from '../RuleTester'; @@ -1066,6 +1066,319 @@ declare const useCallback: unknown>( ) => T; useCallback(async () => {}); `, + { + code: ` +type O = { + 1: () => void; +}; + +const obj: O = { + 1() {}, +}; + `, + }, + { + code: ` +type O = { + 1: () => void; +}; + +const obj: O = { + [1]() {}, +}; + `, + }, + { + code: noFormat` +type O = { + stringLiteral: () => void; +}; + +const obj: O = { + 'stringLiteral'() {}, +}; + `, + }, + { + code: ` +type O = { + computedStringLiteral: () => void; +}; + +const obj: O = { + ['computedStringLiteral']() {}, +}; + `, + }, + { + code: ` +type O = { + [Symbol.iterator]: () => void; +}; + +const obj: O = { + [Symbol.iterator]() {}, +}; + `, + }, + { + code: ` +const staticSymbol = Symbol.for('static symbol'); + +type O = { + [staticSymbol]: () => void; +}; + +const obj: O = { + [staticSymbol]() {}, +}; + `, + }, + { + code: ` +type O = { + 1: () => Promise; +}; + +const obj: O = { + async 1() {}, +}; + `, + }, + { + code: ` +type O = { + 1: () => Promise; +}; + +const obj: O = { + async [1]() {}, +}; + `, + }, + { + code: noFormat` +type O = { + stringLiteral: () => Promise; +}; + +const obj: O = { + async 'stringLiteral'() {}, +}; + `, + }, + { + code: ` +type O = { + computedStringLiteral: () => Promise; +}; + +const obj: O = { + async ['computedStringLiteral']() {}, +}; + `, + }, + { + code: ` +type O = { + [Symbol.iterator]: () => Promise; +}; + +const obj: O = { + async [Symbol.iterator]() {}, +}; + `, + }, + { + code: ` +const staticSymbol = Symbol.for('static symbol'); + +type O = { + [staticSymbol]: () => Promise; +}; + +const obj: O = { + async [staticSymbol]() {}, +}; + `, + }, + { + code: ` +class MyClass { + 1(): void {} +} + +class MySubclass extends MyClass { + 1(): void {} +} + `, + }, + { + code: ` +class MyClass { + 1(): void {} +} + +class MySubclass extends MyClass { + [1](): void {} +} + `, + }, + { + code: noFormat` +class MyClass { + stringLiteral(): void {} +} + +class MySubclass extends MyClass { + 'stringLiteral'(): void {} +} + + `, + }, + { + code: ` +class MyClass { + computedStringLiteral(): void {} +} + +class MySubclass extends MyClass { + ['computedStringLiteral'](): void {} +} + `, + }, + { + code: ` +class MyClass { + [Symbol.iterator](): void {} +} + +class MySubclass extends MyClass { + [Symbol.iterator](): void {} +} + `, + }, + { + code: ` +const staticSymbol = Symbol.for('static symbol'); + +class MyClass { + [staticSymbol](): void {} +} + +class MySubclass extends MyClass { + [staticSymbol](): void {} +} + `, + }, + { + code: ` +interface MyInterface { + 1(): void; +} + +class MySubclass implements MyInterface { + 1(): void {} +} + `, + }, + { + code: ` +interface MyInterface { + 1(): void; +} + +class MySubclass implements MyInterface { + [1](): void {} +} + `, + }, + { + code: noFormat` +interface MyInterface { + stringLiteral(): void; +} + +class MySubclass implements MyInterface { + 'stringLiteral'(): void {} +} + + `, + }, + { + code: ` +interface MyInterface { + computedStringLiteral(): void; +} + +class MySubclass implements MyInterface { + ['computedStringLiteral'](): void {} +} + `, + }, + { + code: ` +interface MyInterface { + [Symbol.iterator](): void; +} + +class MySubclass implements MyInterface { + [Symbol.iterator](): void {} +} + `, + }, + { + code: ` +const staticSymbol = Symbol.for('static symbol'); + +interface MyInterface { + [staticSymbol](): void; +} + +class MySubclass implements MyInterface { + [staticSymbol](): void {} +} + `, + }, + { + code: ` +let a; + +type O = { + [a]: () => Promise; +}; + +const obj: O = { + async [a]() {}, +}; + `, + }, + { + code: ` +let a; + +interface MyInterface { + [a](): void; +} + +class MySubinterfaceExtendsMyInterface implements MyInterface { + [a]: () => Promise; +} + `, + }, + { + code: ` +const staticSymbol = Symbol(); + +interface MyInterface { + [staticSymbol](): Promise; +} + +class MySubclass implements MyInterface { + async [staticSymbol](): Promise {} +} + `, + }, ], invalid: [ @@ -2576,5 +2889,277 @@ const obj: O = { }, ], }, + { + code: ` +type O = { + 1: () => void; +}; + +const obj: O = { + async 1() {}, +}; + `, + errors: [ + { + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type O = { + 1: () => void; +}; + +const obj: O = { + async [1]() {}, +}; + `, + errors: [ + { + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: noFormat` +type O = { + stringLiteral: () => void; +}; + +const obj: O = { + async 'stringLiteral'() {}, +}; + `, + errors: [ + { + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type O = { + computedStringLiteral: () => void; +}; + +const obj: O = { + async ['computedStringLiteral']() {}, +}; + `, + errors: [ + { + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type O = { + [Symbol.iterator]: () => void; +}; + +const obj: O = { + async [Symbol.iterator]() {}, +}; + `, + errors: [ + { + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +class MyClass { + 1(): void {} +} + +class MySubclass extends MyClass { + async 1(): Promise {} +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +class MyClass { + 1(): void {} +} + +class MySubclass extends MyClass { + async [1](): Promise {} +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: noFormat` +class MyClass { + stringLiteral(): void {} +} + +class MySubclass extends MyClass { + async 'stringLiteral'(): Promise {} +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +class MyClass { + computedStringLiteral(): void {} +} + +class MySubclass extends MyClass { + async ['computedStringLiteral'](): Promise {} +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +class MyClass { + [Symbol.asyncIterator](): void {} +} + +class MySubclass extends MyClass { + async [Symbol.asyncIterator](): Promise {} +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +interface MyInterface { + 1(): void; +} + +interface MySubinterface extends MyInterface { + 1(): Promise; +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +interface MyInterface { + 1(): void; +} + +interface MySubinterface extends MyInterface { + [1](): Promise; +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +interface MyInterface { + stringLiteral(): void; +} + +interface MySubinterface extends MyInterface { + 'stringLiteral'(): Promise; +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +interface MyInterface { + computedStringLiteral(): void; +} + +interface MySubinterface extends MyInterface { + ['computedStringLiteral'](): Promise; +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +interface MyInterface { + [Symbol.asyncIterator](): void; +} + +interface MySubinterface extends MyInterface { + [Symbol.asyncIterator](): Promise; +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, + { + code: ` +class MyClass { + ''(): void {} +} + +class MySubclass extends MyClass { + async ''(): Promise {} +} + `, + errors: [ + { + line: 7, + messageId: 'voidReturnInheritedMethod', + }, + ], + }, ], }); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy