Skip to content

Commit d27a9ac

Browse files
fix(eslint-plugin): [no-misused-promises] improve report loc for methods (typescript-eslint#10216)
* fix(eslint-plugin): correctly report errors for async methods returning Promise where void is expected * test add coverage * fix: remove unreachable code * fix: simplify redundant if checks in context.report logic * test: specify error location in voidReturnProperty test cases * fix lint * test: add full loc (line, endLine, column, endColumn) to existing test cases” * test: add new invalid test cases for promise-returning methods without async * fix : test col * fix: log * Update packages/eslint-plugin/src/rules/no-misused-promises.ts Co-authored-by: Kirk Waiblinger <kirk.waiblinger@gmail.com> * refactor(no-misused-promises): improve function type checking in void return validation * test(no-misused-promises): add test cases for isFunction utility usage * test(no-misused-promises): add test cases --------- Co-authored-by: Kirk Waiblinger <kirk.waiblinger@gmail.com>
1 parent 124b65d commit d27a9ac

File tree

2 files changed

+182
-9
lines changed

2 files changed

+182
-9
lines changed

packages/eslint-plugin/src/rules/no-misused-promises.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as ts from 'typescript';
66

77
import {
88
createRule,
9+
getFunctionHeadLoc,
910
getParserServices,
1011
isArrayMethodCallWithPredicate,
1112
isFunction,
@@ -436,10 +437,25 @@ export default createRule<Options, MessageId>({
436437
) &&
437438
returnsThenable(checker, tsNode.initializer)
438439
) {
439-
context.report({
440-
node: node.value,
441-
messageId: 'voidReturnProperty',
442-
});
440+
if (isFunction(node.value)) {
441+
const functionNode = node.value;
442+
if (functionNode.returnType) {
443+
context.report({
444+
node: functionNode.returnType.typeAnnotation,
445+
messageId: 'voidReturnProperty',
446+
});
447+
} else {
448+
context.report({
449+
loc: getFunctionHeadLoc(functionNode, context.sourceCode),
450+
messageId: 'voidReturnProperty',
451+
});
452+
}
453+
} else {
454+
context.report({
455+
node: node.value,
456+
messageId: 'voidReturnProperty',
457+
});
458+
}
443459
}
444460
} else if (ts.isShorthandPropertyAssignment(tsNode)) {
445461
const contextualType = checker.getContextualType(tsNode.name);
@@ -490,10 +506,19 @@ export default createRule<Options, MessageId>({
490506
);
491507

492508
if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) {
493-
context.report({
494-
node: node.value,
495-
messageId: 'voidReturnProperty',
496-
});
509+
const functionNode = node.value as TSESTree.FunctionExpression;
510+
511+
if (functionNode.returnType) {
512+
context.report({
513+
node: functionNode.returnType.typeAnnotation,
514+
messageId: 'voidReturnProperty',
515+
});
516+
} else {
517+
context.report({
518+
loc: getFunctionHeadLoc(functionNode, context.sourceCode),
519+
messageId: 'voidReturnProperty',
520+
});
521+
}
497522
}
498523
return;
499524
}
@@ -513,6 +538,7 @@ export default createRule<Options, MessageId>({
513538
}
514539
return nullThrows(current, NullThrowsReasons.MissingParent);
515540
})();
541+
516542
if (
517543
functionNode.returnType &&
518544
!isPossiblyFunctionType(functionNode.returnType)

packages/eslint-plugin/tests/rules/no-misused-promises.test.ts

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,9 @@ const obj: O = {
14051405
`,
14061406
errors: [
14071407
{
1408+
column: 3,
1409+
endColumn: 12,
1410+
endLine: 4,
14081411
line: 4,
14091412
messageId: 'voidReturnProperty',
14101413
},
@@ -1419,6 +1422,9 @@ const obj: O = {
14191422
`,
14201423
errors: [
14211424
{
1425+
column: 3,
1426+
endColumn: 12,
1427+
endLine: 4,
14221428
line: 4,
14231429
messageId: 'voidReturnProperty',
14241430
},
@@ -1451,6 +1457,9 @@ const obj: O = {
14511457
`,
14521458
errors: [
14531459
{
1460+
column: 3,
1461+
endColumn: 10,
1462+
endLine: 4,
14541463
line: 4,
14551464
messageId: 'voidReturnProperty',
14561465
},
@@ -1472,14 +1481,23 @@ function f(): O {
14721481
`,
14731482
errors: [
14741483
{
1484+
column: 5,
1485+
endColumn: 12,
1486+
endLine: 6,
14751487
line: 6,
14761488
messageId: 'voidReturnProperty',
14771489
},
14781490
{
1491+
column: 5,
1492+
endColumn: 14,
1493+
endLine: 9,
14791494
line: 9,
14801495
messageId: 'voidReturnProperty',
14811496
},
14821497
{
1498+
column: 5,
1499+
endColumn: 6,
1500+
endLine: 10,
14831501
line: 10,
14841502
messageId: 'voidReturnProperty',
14851503
},
@@ -1783,7 +1801,15 @@ const test: ReturnsRecord = () => {
17831801
return { asynchronous: async () => {} };
17841802
};
17851803
`,
1786-
errors: [{ line: 5, messageId: 'voidReturnProperty' }],
1804+
errors: [
1805+
{
1806+
column: 12,
1807+
endColumn: 32,
1808+
endLine: 5,
1809+
line: 5,
1810+
messageId: 'voidReturnProperty',
1811+
},
1812+
],
17871813
},
17881814
{
17891815
code: `
@@ -2429,5 +2455,126 @@ arrayFn<() => void>(
24292455
},
24302456
],
24312457
},
2458+
{
2459+
code: `
2460+
type HasVoidMethod = {
2461+
f(): void;
2462+
};
2463+
2464+
const o: HasVoidMethod = {
2465+
async f() {
2466+
return 3;
2467+
},
2468+
};
2469+
`,
2470+
errors: [
2471+
{
2472+
column: 3,
2473+
endColumn: 10,
2474+
endLine: 7,
2475+
line: 7,
2476+
messageId: 'voidReturnProperty',
2477+
},
2478+
],
2479+
},
2480+
{
2481+
code: `
2482+
type HasVoidMethod = {
2483+
f(): void;
2484+
};
2485+
2486+
const o: HasVoidMethod = {
2487+
async f(): Promise<number> {
2488+
return 3;
2489+
},
2490+
};
2491+
`,
2492+
errors: [
2493+
{
2494+
column: 14,
2495+
endColumn: 29,
2496+
endLine: 7,
2497+
line: 7,
2498+
messageId: 'voidReturnProperty',
2499+
},
2500+
],
2501+
},
2502+
{
2503+
code: `
2504+
type HasVoidMethod = {
2505+
f(): void;
2506+
};
2507+
const obj: HasVoidMethod = {
2508+
f() {
2509+
return Promise.resolve('foo');
2510+
},
2511+
};
2512+
`,
2513+
errors: [
2514+
{
2515+
column: 3,
2516+
endColumn: 4,
2517+
endLine: 6,
2518+
line: 6,
2519+
messageId: 'voidReturnProperty',
2520+
},
2521+
],
2522+
},
2523+
{
2524+
code: `
2525+
type HasVoidMethod = {
2526+
f(): void;
2527+
};
2528+
const obj: HasVoidMethod = {
2529+
f(): Promise<void> {
2530+
throw new Error();
2531+
},
2532+
};
2533+
`,
2534+
errors: [
2535+
{
2536+
column: 8,
2537+
endColumn: 21,
2538+
endLine: 6,
2539+
line: 6,
2540+
messageId: 'voidReturnProperty',
2541+
},
2542+
],
2543+
},
2544+
{
2545+
code: `
2546+
type O = { f: () => void };
2547+
const asyncFunction = async () => 'foo';
2548+
const obj: O = {
2549+
f: asyncFunction,
2550+
};
2551+
`,
2552+
errors: [
2553+
{
2554+
column: 6,
2555+
endColumn: 19,
2556+
endLine: 5,
2557+
line: 5,
2558+
messageId: 'voidReturnProperty',
2559+
},
2560+
],
2561+
},
2562+
{
2563+
code: `
2564+
type O = { f: () => void };
2565+
const obj: O = {
2566+
f: async (): Promise<string> => 'foo',
2567+
};
2568+
`,
2569+
errors: [
2570+
{
2571+
column: 16,
2572+
endColumn: 31,
2573+
endLine: 4,
2574+
line: 4,
2575+
messageId: 'voidReturnProperty',
2576+
},
2577+
],
2578+
},
24322579
],
24332580
});

0 commit comments

Comments
 (0)