# 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; } } } ````