Skip to main content

no-function-declare-after-return

Disallow function declarations that appear after a return statement in the same block scope.

Targeted pattern scope​

This rule reports FunctionDeclaration nodes that appear after a ReturnStatement in the same direct statement list (BlockStatement or Program).

  • return inside switch cases is out of scope for this rule.
  • Function declarations nested inside later if/try/for blocks are not directly targeted by this rule.

Note: This rule targets only FunctionDeclaration nodes. Arrow functions, function expressions, generator functions, and async function expressions assigned to variables are not flagged — those are expression statements and are genuinely unreachable. Use no-unreachable for those cases.

What this rule reports​

JavaScript hoists function declarations to the top of their enclosing scope, which means the following code is syntactically valid and runs without errors:

function publicMethods(obj) {
if (obj instanceof CustomClass) {
return {
get: methodGetter(obj), // ← called before its declaration ❌
};
}
function methodGetter(obj) {
// ← declared after the return
// …
}
}

This rule reports function declarations that rely on that hoisting by appearing after an earlier return in the same block.

Why this rule exists​

Even though this works at runtime, it is a readability trap:

  • A reader scanning the function top-to-bottom will encounter the call to methodGetter before they see its definition.
  • Newcomers unfamiliar with hoisting may not realize methodGetter is even in scope at the call site.
  • Linters such as no-unreachable do not flag this because the declaration is not unreachable — it is hoisted.

This rule enforces that every FunctionDeclaration is placed before any ReturnStatement in the same block scope, making hoisting irrelevant from a readability perspective.

❌ Incorrect​

function outer() {
return 42;
function helper() {} // ← 'helper' should be moved before the return
}
function publicMethods(obj) {
if (obj) {
return {
set: methodSetter(obj),
get: methodGetter(obj),
};
function methodSetter(obj) {
/* … */
} // ← should be before return
function methodGetter(obj) {
/* … */
} // ← should be before return
}
}

✅ Correct​

function outer() {
function helper() {} // ← declared before the return
return helper();
}
function publicMethods(obj) {
function methodSetter(obj) {
/* … */
} // ← before the return
function methodGetter(obj) {
/* … */
} // ← before the return
if (obj) {
return {
set: methodSetter(obj),
get: methodGetter(obj),
};
}
}
// Arrow functions and function expressions after return are NOT flagged —
// use no-unreachable for those.
function outer() {
return 1;
const arrow = () => {}; // not a FunctionDeclaration — not flagged here
}

Behavior and migration notes​

This rule provides an autofix. When triggered, it moves the offending FunctionDeclaration to immediately before the ReturnStatement, preserving the correct indentation level.

When a function declaration has leading comments (including JSDoc), the fixer moves those comments together with the declaration.

This rule has no options.

Original plugin source: eslint-plugin-no-function-declare-after-return.

ESLint flat config example​

import etcMisc from "eslint-plugin-etc-misc";

export default [
{
plugins: { "etc-misc": etcMisc },
rules: {
"etc-misc/no-function-declare-after-return": "warn",
},
},
];

When not to use it​

  • If your team relies on intentional hoisting patterns with thorough documentation, you may disable this rule.
  • If the function declaration lives in a different block scope from the return statement (e.g., hoisted to the outer function while the return is inside an if-block), the fix may require manual review to confirm the intended placement.

Rule catalog ID: R039

Further reading​