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).
returninsideswitchcases is out of scope for this rule.- Function declarations nested inside later
if/try/forblocks are not directly targeted by this rule.
Note: This rule targets only
FunctionDeclarationnodes. Arrow functions, function expressions, generator functions, and async function expressions assigned to variables are not flagged — those are expression statements and are genuinely unreachable. Useno-unreachablefor 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
methodGetterbefore they see its definition. - Newcomers unfamiliar with hoisting may not realize
methodGetteris even in scope at the call site. - Linters such as
no-unreachabledo 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.