Skip to content

Commit 13fe027

Browse files
committed
feat(require-returns-check): add noNativeTypes option to assert async functions do not have native types as return types; fixes #1345
1 parent 7a6b7db commit 13fe027

File tree

7 files changed

+86
-21
lines changed

7 files changed

+86
-21
lines changed

.README/rules/require-returns-check.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Will also report `@returns {void}` and `@returns {undefined}` if `exemptAsync`
1111
is set to `false` and a non-`undefined` value is returned or a resolved value
1212
is found. Also reports if `@returns {never}` is discovered with a return value.
1313

14+
Will report if native types are specified for `@returns` on an async function.
15+
1416
Will also report if multiple `@returns` tags are present.
1517

1618
## Options
@@ -24,7 +26,7 @@ Will also report if multiple `@returns` tags are present.
2426
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
2527
|Tags|`returns`|
2628
|Aliases|`return`|
27-
|Options|`exemptAsync`, `exemptGenerators`, `reportMissingReturnForUndefinedTypes`|
29+
|Options|`exemptAsync`, `exemptGenerators`, `noNativeTypes`, `reportMissingReturnForUndefinedTypes`|
2830
|Recommended|true|
2931

3032
## Failing examples

docs/rules/require-returns-check.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* [Options](#user-content-require-returns-check-options)
66
* [`exemptAsync`](#user-content-require-returns-check-options-exemptasync)
77
* [`exemptGenerators`](#user-content-require-returns-check-options-exemptgenerators)
8+
* [`noNativeTypes`](#user-content-require-returns-check-options-nonativetypes)
89
* [`reportMissingReturnForUndefinedTypes`](#user-content-require-returns-check-options-reportmissingreturnforundefinedtypes)
910
* [Context and settings](#user-content-require-returns-check-context-and-settings)
1011
* [Failing examples](#user-content-require-returns-check-failing-examples)
@@ -20,6 +21,8 @@ Will also report `@returns {void}` and `@returns {undefined}` if `exemptAsync`
2021
is set to `false` and a non-`undefined` value is returned or a resolved value
2122
is found. Also reports if `@returns {never}` is discovered with a return value.
2223

24+
Will report if native types are specified for `@returns` on an async function.
25+
2326
Will also report if multiple `@returns` tags are present.
2427

2528
<a name="user-content-require-returns-check-options"></a>
@@ -54,6 +57,13 @@ option is therefore `true` by default in `typescript` mode (in "jsdoc" mode,
5457
one might be more likely to take advantage of `@yields`). Set it to `false`
5558
if you wish for a missing `return` to be flagged regardless.
5659

60+
<a name="user-content-require-returns-check-options-nonativetypes"></a>
61+
<a name="require-returns-check-options-nonativetypes"></a>
62+
### <code>noNativeTypes</code>
63+
64+
Whether to check that async functions do not
65+
indicate they return non-native types. Defaults to `true`.
66+
5767
<a name="user-content-require-returns-check-options-reportmissingreturnforundefinedtypes"></a>
5868
<a name="require-returns-check-options-reportmissingreturnforundefinedtypes"></a>
5969
### <code>reportMissingReturnForUndefinedTypes</code>
@@ -74,7 +84,7 @@ Unlike `require-returns`, with this option in the rule, one can
7484
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
7585
|Tags|`returns`|
7686
|Aliases|`return`|
77-
|Options|`exemptAsync`, `exemptGenerators`, `reportMissingReturnForUndefinedTypes`|
87+
|Options|`exemptAsync`, `exemptGenerators`, `noNativeTypes`, `reportMissingReturnForUndefinedTypes`|
7888
|Recommended|true|
7989

8090
<a name="user-content-require-returns-check-failing-examples"></a>
@@ -206,7 +216,7 @@ function quux() {
206216

207217
/**
208218
* Description.
209-
* @returns {string}
219+
* @returns {SomeType}
210220
*/
211221
async function foo() {
212222
return new Promise(resolve => resolve());
@@ -401,6 +411,15 @@ function foo() {
401411
}
402412
}
403413
// Message: JSDoc @returns declaration present but return expression not available in function.
414+
415+
/**
416+
* @returns {number}
417+
*/
418+
async function quux (foo) {
419+
420+
}
421+
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
422+
// Message: Function is async or otherwise returns a Promise but the return type is a native type.
404423
````
405424

406425

src/jsdocUtils.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,7 @@ const mayBeUndefinedTypeTag = (tag, mode) => {
962962
// We do not traverse deeply as it could be, e.g., `Promise<void>`
963963
parsedTypes &&
964964
parsedTypes.type === 'JsdocTypeUnion' &&
965-
parsedTypes.elements.find((elem) => {
965+
parsedTypes.elements.some((elem) => {
966966
return elem.type === 'JsdocTypeUndefined' ||
967967
elem.type === 'JsdocTypeName' && elem.value === 'void';
968968
})) {
@@ -1845,6 +1845,21 @@ const getRegexFromString = (regexString, requiredFlags) => {
18451845
return new RegExp(regex, flags);
18461846
};
18471847

1848+
const strictNativeTypes = [
1849+
'undefined',
1850+
'null',
1851+
'boolean',
1852+
'number',
1853+
'bigint',
1854+
'string',
1855+
'symbol',
1856+
'object',
1857+
'Array',
1858+
'Function',
1859+
'Date',
1860+
'RegExp',
1861+
];
1862+
18481863
export {
18491864
comparePaths,
18501865
dropPathSegmentQuotes,
@@ -1885,6 +1900,7 @@ export {
18851900
parseClosureTemplateTag,
18861901
pathDoesNotBeginWith,
18871902
setTagStructure,
1903+
strictNativeTypes,
18881904
tagMightHaveEitherTypeOrNamePosition,
18891905
tagMightHaveNamepath,
18901906
tagMightHaveNamePosition,

src/rules.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,6 +2317,11 @@ export interface Rules {
23172317
* if you wish for a missing `return` to be flagged regardless.
23182318
*/
23192319
exemptGenerators?: boolean;
2320+
/**
2321+
* Whether to check that async functions do not
2322+
* indicate they return non-native types. Defaults to `true`.
2323+
*/
2324+
noNativeTypes?: boolean;
23202325
/**
23212326
* If `true` and no return or
23222327
* resolve value is found, this setting will even insist that reporting occur

src/rules/checkTypes.js

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
11
import {
22
buildRejectOrPreferRuleDefinition,
33
} from '../buildRejectOrPreferRuleDefinition.js';
4-
5-
const strictNativeTypes = [
6-
'undefined',
7-
'null',
8-
'boolean',
9-
'number',
10-
'bigint',
11-
'string',
12-
'symbol',
13-
'object',
14-
'Array',
15-
'Function',
16-
'Date',
17-
'RegExp',
18-
];
4+
import {
5+
strictNativeTypes,
6+
} from '../jsdocUtils.js';
197

208
/**
219
* @callback CheckNativeTypes

src/rules/requireReturnsCheck.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import iterateJsdoc from '../iterateJsdoc.js';
2+
import {
3+
strictNativeTypes,
4+
} from '../jsdocUtils.js';
25

36
/**
47
* @param {import('../iterateJsdoc.js').Utils} utils
@@ -43,14 +46,16 @@ export default iterateJsdoc(({
4346
const {
4447
exemptAsync = true,
4548
exemptGenerators = settings.mode === 'typescript',
49+
noNativeTypes = true,
4650
reportMissingReturnForUndefinedTypes = false,
4751
} = context.options[0] || {};
4852

4953
if (canSkip(utils, settings)) {
5054
return;
5155
}
5256

53-
if (exemptAsync && utils.isAsync()) {
57+
const isAsync = utils.isAsync();
58+
if (exemptAsync && isAsync) {
5459
return;
5560
}
5661

@@ -92,6 +97,11 @@ export default iterateJsdoc(({
9297
return;
9398
}
9499

100+
if (noNativeTypes && isAsync && strictNativeTypes.includes(type)) {
101+
report('Function is async or otherwise returns a Promise but the return type is a native type.');
102+
return;
103+
}
104+
95105
// In case a return value is declared in JSDoc, we also expect one in the code.
96106
if (
97107
!returnNever &&
@@ -147,6 +157,11 @@ one might be more likely to take advantage of \`@yields\`). Set it to \`false\`
147157
if you wish for a missing \`return\` to be flagged regardless.`,
148158
type: 'boolean',
149159
},
160+
noNativeTypes: {
161+
description: `Whether to check that async functions do not
162+
indicate they return non-native types. Defaults to \`true\`.`,
163+
type: 'boolean',
164+
},
150165
reportMissingReturnForUndefinedTypes: {
151166
default: false,
152167
description: `If \`true\` and no return or

test/rules/assertions/requireReturnsCheck.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ export default /** @type {import('../index.js').TestCases} */ ({
277277
code: `
278278
/**
279279
* Description.
280-
* @returns {string}
280+
* @returns {SomeType}
281281
*/
282282
async function foo() {
283283
return new Promise(resolve => resolve());
@@ -664,7 +664,27 @@ export default /** @type {import('../index.js').TestCases} */ ({
664664
},
665665
],
666666
},
667+
{
668+
code: `
669+
/**
670+
* @returns {number}
671+
*/
672+
async function quux (foo) {
667673
674+
}
675+
`,
676+
errors: [
677+
{
678+
line: 2,
679+
message: 'Function is async or otherwise returns a Promise but the return type is a native type.',
680+
},
681+
],
682+
options: [
683+
{
684+
exemptAsync: false,
685+
},
686+
],
687+
},
668688
],
669689
valid: [
670690
{

0 commit comments

Comments
 (0)