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)
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