Content-Length: 6856 | pFad | http://github.com/typescript-eslint/typescript-eslint/pull/4620.diff
thub.com
diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts
index 7f08402334cc..b50ad53901ef 100644
--- a/packages/eslint-plugin/src/rules/no-misused-promises.ts
+++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts
@@ -394,9 +394,13 @@ function voidFunctionParams(
checker: ts.TypeChecker,
node: ts.CallExpression | ts.NewExpression,
): Set {
+ const thenableReturnIndices = new Set();
const voidReturnIndices = new Set();
const type = checker.getTypeAtLocation(node.expression);
+ // We can't use checker.getResolvedSignature because it prefers an early '() => void' over a later '() => Promise'
+ // See https://github.com/microsoft/TypeScript/issues/48077
+
for (const subType of tsutils.unionTypeParts(type)) {
// Standard function calls and `new` have two different types of signatures
const signatures = ts.isCallExpression(node)
@@ -408,50 +412,94 @@ function voidFunctionParams(
parameter,
node.expression,
);
- if (isVoidReturningFunctionType(checker, node.expression, type)) {
+ if (isThenableReturningFunctionType(checker, node.expression, type)) {
+ thenableReturnIndices.add(index);
+ } else if (
+ !thenableReturnIndices.has(index) &&
+ isVoidReturningFunctionType(checker, node.expression, type)
+ ) {
voidReturnIndices.add(index);
}
}
}
}
+ for (const index of thenableReturnIndices) {
+ voidReturnIndices.delete(index);
+ }
+
return voidReturnIndices;
}
-// Returns true if given type is a void-returning function.
+/**
+ * @returns Whether any call signature of the type has a thenable return type.
+ */
+function anySignatureIsThenableType(
+ checker: ts.TypeChecker,
+ node: ts.Node,
+ type: ts.Type,
+): boolean {
+ for (const signature of type.getCallSignatures()) {
+ const returnType = signature.getReturnType();
+ if (tsutils.isThenableType(checker, node, returnType)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @returns Whether type is a thenable-returning function.
+ */
+function isThenableReturningFunctionType(
+ checker: ts.TypeChecker,
+ node: ts.Node,
+ type: ts.Type,
+): boolean {
+ for (const subType of tsutils.unionTypeParts(type)) {
+ if (anySignatureIsThenableType(checker, node, subType)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @returns Whether type is a void-returning function.
+ */
function isVoidReturningFunctionType(
checker: ts.TypeChecker,
node: ts.Node,
type: ts.Type,
): boolean {
- let hasVoidReturningFunction = false;
- let hasThenableReturningFunction = false;
for (const subType of tsutils.unionTypeParts(type)) {
for (const signature of subType.getCallSignatures()) {
const returnType = signature.getReturnType();
+
+ // If a certain positional argument accepts both thenable and void returns,
+ // a promise-returning function is valid
+ if (tsutils.isThenableType(checker, node, returnType)) {
+ return false;
+ }
+
if (tsutils.isTypeFlagSet(returnType, ts.TypeFlags.Void)) {
- hasVoidReturningFunction = true;
- } else if (tsutils.isThenableType(checker, node, returnType)) {
- hasThenableReturningFunction = true;
+ return true;
}
}
}
- // If a certain positional argument accepts both thenable and void returns,
- // a promise-returning function is valid
- return hasVoidReturningFunction && !hasThenableReturningFunction;
+ return false;
}
-// Returns true if the expression is a function that returns a thenable
+/**
+ * @returns Whether expression is a function that returns a thenable.
+ */
function returnsThenable(checker: ts.TypeChecker, node: ts.Node): boolean {
const type = checker.getApparentType(checker.getTypeAtLocation(node));
- for (const subType of tsutils.unionTypeParts(type)) {
- for (const signature of subType.getCallSignatures()) {
- const returnType = signature.getReturnType();
- if (tsutils.isThenableType(checker, node, returnType)) {
- return true;
- }
- }
+ if (anySignatureIsThenableType(checker, node, type)) {
+ return true;
}
return false;
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 961a21d4c892..1cc0576fb300 100644
--- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts
+++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts
@@ -252,6 +252,58 @@ const Component: any = () => null;
`,
filename: 'react.tsx',
},
+ {
+ code: `
+interface ItLike {
+ (name: string, callback: () => Promise): void;
+ (name: string, callback: () => void): void;
+}
+
+declare const it: ItLike;
+
+it('', async () => {});
+ `,
+ },
+ {
+ code: `
+interface ItLike {
+ (name: string, callback: () => void): void;
+ (name: string, callback: () => Promise): void;
+}
+
+declare const it: ItLike;
+
+it('', async () => {});
+ `,
+ },
+ {
+ code: `
+interface ItLike {
+ (name: string, callback: () => void): void;
+}
+interface ItLike {
+ (name: string, callback: () => Promise): void;
+}
+
+declare const it: ItLike;
+
+it('', async () => {});
+ `,
+ },
+ {
+ code: `
+interface ItLike {
+ (name: string, callback: () => Promise): void;
+}
+interface ItLike {
+ (name: string, callback: () => void): void;
+}
+
+declare const it: ItLike;
+
+it('', async () => {});
+ `,
+ },
],
invalid: [
@@ -687,5 +739,63 @@ const Component = (obj: O) => null;
},
],
},
+ {
+ code: `
+interface ItLike {
+ (name: string, callback: () => number): void;
+ (name: string, callback: () => void): void;
+}
+
+declare const it: ItLike;
+
+it('', async () => {});
+ `,
+ errors: [
+ {
+ line: 9,
+ messageId: 'voidReturnArgument',
+ },
+ ],
+ },
+ {
+ code: `
+interface ItLike {
+ (name: string, callback: () => number): void;
+}
+interface ItLike {
+ (name: string, callback: () => void): void;
+}
+
+declare const it: ItLike;
+
+it('', async () => {});
+ `,
+ errors: [
+ {
+ line: 11,
+ messageId: 'voidReturnArgument',
+ },
+ ],
+ },
+ {
+ code: `
+interface ItLike {
+ (name: string, callback: () => void): void;
+}
+interface ItLike {
+ (name: string, callback: () => number): void;
+}
+
+declare const it: ItLike;
+
+it('', async () => {});
+ `,
+ errors: [
+ {
+ line: 11,
+ messageId: 'voidReturnArgument',
+ },
+ ],
+ },
],
});
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/typescript-eslint/typescript-eslint/pull/4620.diff
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy