# require-returns-check
* [Options](#user-content-require-returns-check-options)
* [`exemptAsync`](#user-content-require-returns-check-options-exemptasync)
* [`exemptGenerators`](#user-content-require-returns-check-options-exemptgenerators)
* [`noNativeTypes`](#user-content-require-returns-check-options-nonativetypes)
* [`reportMissingReturnForUndefinedTypes`](#user-content-require-returns-check-options-reportmissingreturnforundefinedtypes)
* [Context and settings](#user-content-require-returns-check-context-and-settings)
* [Failing examples](#user-content-require-returns-check-failing-examples)
* [Passing examples](#user-content-require-returns-check-passing-examples)
Requires a return statement (or non-`undefined` Promise resolve value)
be present in a
function body if a `@returns` tag (without a `void` or `undefined` type)
is specified in the function's JSDoc comment block.
Will also report `@returns {void}` and `@returns {undefined}` if `exemptAsync`
is set to `false` and a non-`undefined` value is returned or a resolved value
is found. Also reports if `@returns {never}` is discovered with a return value.
Will report if native types are specified for `@returns` on an async function.
Will also report if multiple `@returns` tags are present.
If a `@returns` type references a typedef in the same source document and that
typedef may be `void` or `undefined`, the rule treats the return type as
allowing an absent return value unless `reportMissingReturnForUndefinedTypes`
is set to `true`.
## Options
A single options object has the following properties.
### exemptAsync
By default, functions which return a `Promise` that are not
detected as resolving with a non-`undefined` value and `async` functions
(even ones that do not explicitly return a value, as these are returning a
`Promise` implicitly) will be exempted from reporting by this rule.
If you wish to insist that only `Promise`'s which resolve to
non-`undefined` values or `async` functions with explicit `return`'s will
be exempted from reporting (i.e., that `async` functions can be reported
if they lack an explicit (non-`undefined`) `return` when a `@returns` is
present), you can set `exemptAsync` to `false` on the options object.
### exemptGenerators
Because a generator might be labeled as having a
`IterableIterator` `@returns` value (along with an iterator type
corresponding to the type of any `yield` statements), projects might wish to
leverage `@returns` in generators even without a `return` statement. This
option is therefore `true` by default in `typescript` mode (in "jsdoc" mode,
one might be more likely to take advantage of `@yields`). Set it to `false`
if you wish for a missing `return` to be flagged regardless.
### noNativeTypes
Whether to check that async functions do not
indicate they return non-native types. Defaults to `true`.
### reportMissingReturnForUndefinedTypes
If `true` and no return or
resolve value is found, this setting will even insist that reporting occur
with `void` or `undefined` (including as an indicated `Promise` type).
Unlike `require-returns`, with this option in the rule, one can
*discourage* the labeling of `undefined` types. Defaults to `false`.
## Context and settings
|||
|---|---|
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
|Tags|`returns`|
|Aliases|`return`|
|Options|`exemptAsync`, `exemptGenerators`, `noNativeTypes`, `reportMissingReturnForUndefinedTypes`|
|Recommended|true|
## Failing examples
The following patterns are considered problems:
````ts
/**
* @returns
*/
function quux (foo) {
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @return
*/
function quux (foo) {
}
// Settings: {"jsdoc":{"tagNamePreference":{"returns":"return"}}}
// Message: JSDoc @return declaration present but return expression not available in function.
/**
* @returns
*/
const quux = () => {}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {undefined} Foo.
* @returns {String} Foo.
*/
function quux () {
return foo;
}
// Message: Found more than one @returns declaration.
const language = {
/**
* @param {string} name
* @returns {string}
*/
get name() {
this._name = name;
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
class Foo {
/**
* @returns {string}
*/
bar () {
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns
*/
function quux () {
}
// Settings: {"jsdoc":{"tagNamePreference":{"returns":false}}}
// Message: Unexpected tag `@returns`
/**
* @returns {string}
*/
function f () {
function g() {
return 'foo'
}
() => {
return 5
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {Promise}
*/
async function quux() {}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {IterableIterator}
*/
function * quux() {}
// Settings: {"jsdoc":{"mode":"jsdoc"}}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {IterableIterator}
*/
function * quux() {}
// Settings: {"jsdoc":{"mode":"typescript"}}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptGenerators":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {Promise}
*/
function quux() {
return new Promise((resolve, reject) => {})
}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {Promise}
*/
function quux() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
});
})
}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* Description.
* @returns {SomeType}
*/
async function foo() {
return new Promise(resolve => resolve());
}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* Description.
* @returns {void}
*/
async function foo() {
return new Promise(resolve => resolve());
}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false,"reportMissingReturnForUndefinedTypes":true}]
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns { void } Foo.
*/
function quux () {}
// "jsdoc/require-returns-check": ["error"|"warn", {"reportMissingReturnForUndefinedTypes":true}]
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {never} Foo.
*/
function quux () {
return undefined;
}
// Message: JSDoc @returns declaration set with "never" but return expression is present in function.
/**
* @returns {never}
*/
function quux (foo) {
return foo;
}
// Message: JSDoc @returns declaration set with "never" but return expression is present in function.
/**
* Reads a test fixture.
*
* @param path The path to resolve relative to the fixture base. It will be normalized for the
* operating system.
*
* @returns The file contents as buffer.
*/
export function readFixture(path: string): void;
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* Reads a test fixture.
*
* @param path The path to resolve relative to the fixture base. It will be normalized for the
* operating system.
*
* @returns The file contents as buffer.
*/
export function readFixture(path: string);
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {SomeType}
*/
function quux (path) {
if (true) {
return;
}
return 15;
};
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* Reads a test fixture.
*
* @param path The path to resolve relative to the fixture base. It will be normalized for the
* operating system.
*
* @returns The file contents as buffer.
*/
export function readFixture(path: string): void {
return;
};
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {true}
*/
function quux () {
if (true) {
return true;
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {true}
*/
function quux () {
if (true) {
} else {
return;
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {true}
*/
function quux (someVar) {
switch (someVar) {
case 1:
return true;
case 2:
return;
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {boolean}
*/
const quux = (someVar) => {
if (someVar) {
return true;
}
};
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {true}
*/
function quux () {
try {
return true;
} catch (error) {
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {true}
*/
function quux () {
try {
return true;
} catch (error) {
return true;
} finally {
return;
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {true}
*/
function quux () {
if (true) {
throw new Error('abc');
}
throw new Error('def');
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {SomeType} Baz.
*/
function foo() {
switch (true) {
default:
switch (false) {
default: return;
}
return "baz";
}
};
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {number}
*/
function foo() {
let n = 1;
while (n > 0.5) {
n = Math.random();
if (n < 0.2) {
return n;
}
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @returns {number}
*/
async function quux (foo) {
}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
// Message: Function is async or otherwise returns a Promise but the return type is a native type.
/**
* @typedef {{ ok: boolean }} MaybeResult
*/
/**
* @returns {MaybeResult} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return;
}
};
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @typedef {{ ok: boolean }} MaybeResult
*/
/**
* @returns {MaybeResult|string} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return;
}
};
// Message: JSDoc @returns declaration present but return expression not available in function.
/**
* @typedef {{ ok: boolean }} MaybeResult
*/
/**
* @returns {Array} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return;
}
};
// Message: JSDoc @returns declaration present but return expression not available in function.
````
## Passing examples
The following patterns are not considered problems:
````ts
/**
* @returns Foo.
*/
function quux () {
return foo;
}
/**
* @returns {string} Foo.
*/
function quux () {
return foo;
}
/**
*
*/
function quux () {
}
/**
* @returns {SomeType} Foo.
*/
const quux = () => foo;
/**
* @returns {undefined} Foo.
*/
function quux () {}
/**
* @returns { void } Foo.
*/
function quux () {}
/**
* @returns {Promise}
*/
async function quux() {}
/**
* @returns {Promise}
*/
const quux = async function () {}
/**
* @returns {Promise}
*/
const quux = async () => {}
/**
* @returns Foo.
* @abstract
*/
function quux () {
throw new Error('must be implemented by subclass!');
}
/**
* @returns Foo.
* @virtual
*/
function quux () {
throw new Error('must be implemented by subclass!');
}
/**
* @returns Foo.
* @constructor
*/
function quux () {
}
/**
* @interface
*/
class Foo {
/**
* @returns {string}
*/
bar () {
}
}
/**
* @record
*/
class Foo {
/**
* @returns {string}
*/
bar () {
}
}
// Settings: {"jsdoc":{"mode":"closure"}}
/**
* @returns {undefined} Foo.
*/
function quux () {
}
/**
* @returns {void} Foo.
*/
function quux () {
}
/**
* @returns {void} Foo.
*/
function quux () {
return undefined;
}
/**
* @returns {never} Foo.
*/
function quux () {
}
/**
* @returns {void} Foo.
*/
function quux () {
return;
}
/**
*
*/
function quux () {
return undefined;
}
/**
*
*/
function quux () {
return;
}
/**
* @returns {true}
*/
function quux () {
try {
return true;
} catch (err) {
}
return true;
}
/**
* @returns {true}
*/
function quux () {
try {
} finally {
return true;
}
return true;
}
/**
* @returns {true}
*/
function quux () {
try {
something();
} catch (err) {
return true;
}
return true;
}
/**
* @returns {true}
*/
function quux () {
switch (true) {
case 'abc':
return true;
}
return true;
}
/**
* @returns {true}
*/
function quux () {
for (const i of abc) {
return true;
}
return true;
}
/**
* @returns {true}
*/
function quux () {
for (const a in b) {
return true;
}
}
/**
* @returns {true}
*/
function quux () {
for (const a of b) {
return true;
}
}
/**
* @returns {true}
*/
function quux () {
loop: for (const a of b) {
return true;
}
}
/**
* @returns {true}
*/
function quux () {
for (let i=0; i}
*/
async function quux() {
return 5;
}
/**
* @returns {Promise}
*/
async function quux() {
return 5;
}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
/**
* @returns {Promise}
*/
function quux() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
});
})
}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptAsync":false}]
/**
* Description.
* @returns {void}
*/
async function foo() {
return new Promise(resolve => resolve());
}
// "jsdoc/require-returns-check": ["error"|"warn", {"reportMissingReturnForUndefinedTypes":true}]
/**
* @returns { void } Foo.
*/
function quux () {
return undefined;
}
// "jsdoc/require-returns-check": ["error"|"warn", {"reportMissingReturnForUndefinedTypes":true}]
/**
* @returns { string } Foo.
*/
function quux () {
return 'abc';
}
// "jsdoc/require-returns-check": ["error"|"warn", {"reportMissingReturnForUndefinedTypes":true}]
/**
* @returns {IterableIterator}
*/
function * quux() {}
// Settings: {"jsdoc":{"mode":"typescript"}}
/**
* @returns {IterableIterator}
*/
function * quux() {}
// Settings: {"jsdoc":{"mode":"jsdoc"}}
// "jsdoc/require-returns-check": ["error"|"warn", {"exemptGenerators":true}]
/**
* @param {unknown} val
* @returns { asserts val is number }
*/
function assertNumber(val) {
assert(typeof val === 'number');
}
/**
* Reads a test fixture.
*
* @param path The path to resolve relative to the fixture base. It will be normalized for the
* operating system.
*
* @returns The file contents as buffer.
*/
export function readFixture(path: string): Promise;
/**
* Reads a test fixture.
*
* @param path The path to resolve relative to the fixture base. It will be normalized for the
* operating system.
*
* @returns {SomeType} The file contents as buffer.
*/
export function readFixture(path: string): Promise;
/**
* Reads a test fixture.
*
* @param path The path to resolve relative to the fixture base. It will be normalized for the
* operating system.
*
* @returns The file contents as buffer.
*/
export function readFixture(path: string): Promise {
return new Promise(() => {});
}
/**
* Reads a test fixture.
*
* @param path The path to resolve relative to the fixture base. It will be normalized for the
* operating system.
*
* @returns {void} The file contents as buffer.
*/
export function readFixture(path: string);
/**
* @returns {SomeType}
*/
function quux (path) {
if (true) {
return 5;
}
return 15;
};
/**
* @returns {SomeType} Foo.
*/
const quux = () => new Promise((resolve) => {
resolve(3);
});
/**
* @returns {SomeType} Foo.
*/
const quux = function () {
return new Promise((resolve) => {
resolve(3);
});
};
/**
* @returns {true}
*/
function quux () {
if (true) {
return true;
}
throw new Error('Fail');
}
/**
* @returns Baz.
*/
function foo() {
switch (true) {
default:
switch (false) {
default: break;
}
return "baz";
}
};
/**
* Return a V1 style query identifier.
*
* @param {string} id - The query identifier.
* @returns {string} V1 style query identifier.
*/
function v1QueryId(id) {
switch (id) {
case 'addq':
case 'aliq':
case 'locq':
return id.substring(3);
case 'lost':
return id.substring(4);
default:
return id;
}
}
/**
* Parses the required header fields for the given SIP message.
*
* @param {string} logPrefix - The log prefix.
* @param {string} sipMessage - The SIP message.
* @param {string[]} headers - The header fields to be parsed.
* @returns {object} Object with parsed header fields.
*/
function parseSipHeaders(logPrefix, sipMessage, headers) {
try {
return esappSip.parseHeaders(sipMessage, headers);
} catch (err) {
logger.error(logPrefix, 'Failed to parse');
return {};
}
}
/**
* @returns {true}
*/
function quux () {
try {
} catch (error) {
} finally {
return true;
}
}
/** Returns true.
*
* @returns {boolean} true
*/
function getTrue() {
try {
return true;
} finally {
console.log('returning...');
}
}
/**
* Maybe return a boolean.
* @returns {boolean|void} true, or undefined.
*/
function maybeTrue() {
if (Math.random() > 0.5) {
return true;
}
}
/**
* @param {string} name
*
* @typedef {{ loadTime: number; runTime: number; totalTime: number } | void} PerfResult
* @returns {PerfResult} Perf result
*/
const perfCase = name => {
const loadStartTime = performance.now();
let syncFn;
try {
syncFn = require(`./${name}.cjs`);
} catch {
return;
}
const loadTime = performance.now() - loadStartTime;
let i = RUN_TIMES;
const runStartTime = performance.now();
while (i-- > 0) {
syncFn(__filename);
}
const runTime = performance.now() - runStartTime;
return {
loadTime,
runTime,
totalTime: runTime + loadTime,
};
};
/**
* @typedef {{ ok: boolean } | void} MaybeResult
*/
/**
* @returns {MaybeResult} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return { ok: true };
}
};
/**
* @typedef {{ ok: boolean } | void} MaybeResult
*/
/**
* @returns {MaybeResult} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return { ok: true };
}
};
// Settings: {"jsdoc":{"mode":"permissive"}}
/**
* @returns {MaybeResult} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return { ok: true };
}
};
/**
* @typedef {{ ok: boolean } | void} MaybeResult
*/
/**
* @typedef {{ ok: boolean } | undefined} MaybeResult
*/
/**
* @returns {MaybeResult|string} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return { ok: true };
}
};
/**
* @typedef {{ ok: boolean }} MaybeResult
*/
/**
* @returns {MaybeResult|void} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return { ok: true };
}
};
/**
* @typedef {{ ok: boolean }} MaybeResult
*/
/**
* @returns {(MaybeResult|void)} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return { ok: true };
}
};
/**
* @typedef {{ ok: boolean }} MaybeResult
*/
/**
* @returns {(MaybeResult|undefined)} Result.
*/
const maybeResult = () => {
if (Math.random() > 0.5) {
return { ok: true };
}
};
/**
* @param {AST} astNode
* @returns {AST}
*/
const getTSFunctionComment = function (astNode) {
switch (greatGrandparent.type) {
case 'VariableDeclarator':
if (greatGreatGrandparent.type === 'VariableDeclaration') {
return greatGreatGrandparent;
}
default:
return astNode;
}
};
const f =
/**
* Description.
*
* @returns Result.
*/
() => {
return function () {};
};
/**
* Description.
*
* @returns Result.
*/
export function f(): string {
return "";
interface I {}
}
/**
* @param {boolean} bar A fun variable.
* @returns {*} Anything at all!
*/
function foo( bar ) {
if ( bar ) {
return functionWithUnknownReturnType();
}
}
/**
* @returns Baz.
*/
function foo() {
switch (true) {
default:
switch (false) {
default: return;
}
return "baz";
}
};
/**
* @returns
*/
const quux = (someVar) => {
if (someVar) {
return true;
}
};
/**
* @returns {number}
*/
function foo() {
while (true) {
const n = Math.random();
if (n < 0.5) {
return n;
}
}
}
/**
* @returns {number}
*/
function foo() {
for (;;) {
const n = Math.random();
if (n < 0.5) {
return n;
}
}
}
````