Skip to main content

no-use-before-define

In JavaScript, variables and functions can be used before they’re declared due to hoisting. This rule enforces that variables are declared before use.
Rule Type: Problem
Fixable: No

Why This Rule Exists

Using variables before declaration can be confusing. ES6 let and const even throw ReferenceError when accessed before declaration (temporal dead zone).
alert(a); // Works due to hoisting, but confusing
var a = 10;

alert(b); // ReferenceError: temporal dead zone
let b = 20;

Rule Details

This rule warns when it encounters references to identifiers that haven’t been declared yet.

Examples

Incorrect Code

// Using var before declaration
alert(a);
var a = 10;

// Calling function before declaration
f();
function f() {}

// Referencing in function before declaration
function g() {
    return b;
}
var b = 1;

// let/const temporal dead zone
{
    alert(c);
    let c = 1;
}

// Class extending itself
{
    class C extends C {}
}

// Class static field referencing class
{
    class C {
        static x = "foo";
        [C.x]() {}
    }
}

// Export before definition
export { foo };
const foo = 1;

Correct Code

// Declare before use
var a;
a = 10;
alert(a);

// Function declaration before call
function f() {}
f(1);

// Variable declared before function uses it
var b = 1;
function g() {
    return b;
}

// let/const declared before use
{
    let c;
    c++;
}

// Class can reference itself in static initializer
{
    class C {
        static x = C;
    }
}

// Definition before export
const foo = 1;
export { foo };

Options

This rule has detailed options for controlling what it checks:
{
    "no-use-before-define": ["error", {
        "functions": true,
        "classes": true,
        "variables": true,
        "allowNamedExports": false,
        "enums": true,
        "typedefs": true,
        "ignoreTypeReferences": true
    }]
}

functions

Type: boolean
Default: true
Check function declarations. When false, allows calling functions before they’re declared.
// With { "functions": false }
f(); // OK
function f() {}
Function declarations are hoisted, so it’s safe to set this to false. However, leaving it true enforces a clearer code style.

classes

Type: boolean
Default: true
Check class declarations. When false, allows referencing classes before declaration in upper scopes.
// With { "classes": false }
function foo() {
    return new A(); // OK if A is declared later
}
class A {}
Classes are NOT hoisted. Setting classes: false can be dangerous.

variables

Type: boolean
Default: true
Check variable declarations. When false, ignores references from upper scopes.
// With { "variables": false }
function baz() {
    console.log(foo); // OK if foo declared later in outer scope
}
var foo = 1;

allowNamedExports

Type: boolean
Default: false
When true, allows references in export {} declarations (they’re safe even if declared later).
// With { "allowNamedExports": true }
export { a, b, f, C }; // OK

const a = 1;
let b;
function f() {}
class C {}

TypeScript Options

enums

Type: boolean
Default: true
Check TypeScript enum references.
// With { "enums": true } - INCORRECT
const x = Foo.FOO;
enum Foo {
  FOO,
}

// CORRECT
enum Foo {
  FOO,
}
const x = Foo.FOO;

typedefs

Type: boolean
Default: true
Check TypeScript type alias and interface references.
// With { "typedefs": true } - INCORRECT  
let myVar: StringOrNumber;
type StringOrNumber = string | number;

// CORRECT
type StringOrNumber = string | number;
let myVar: StringOrNumber;

ignoreTypeReferences

Type: boolean
Default: true
When true, ignores all type references (in type annotations, assertions, etc.).
// With { "ignoreTypeReferences": true } - OK
let var1: StringOrNumber; // Type reference ignored
type StringOrNumber = string | number;

"nofunc" Shorthand

Shorthand for { "functions": false, "classes": true, "variables": true }:
{
  "no-use-before-define": ["error", "nofunc"]
}

Common Patterns

Mutual Recursion

// With { "functions": true } - can't do mutual recursion
function isOdd(n) {
    if (n === 0) return false;
    return isEven(n - 1); // Error: isEven not defined yet
}
function isEven(n) {
    if (n === 0) return true;
    return isOdd(n - 1);
}

// Solution: Set functions: false
// Or restructure to declare both first:
const isOdd = function(n) {
    if (n === 0) return false;
    return isEven(n - 1);
};
const isEven = function(n) {
    if (n === 0) return true;
    return isOdd(n - 1);
};

Temporal Dead Zone

// This throws ReferenceError, rule helps catch it
{
    console.log(x); // TDZ - error at runtime!
    let x = 5;
}

// Rule prevents this mistake
{
    let x = 5;
    console.log(x); // Safe
}

Export Patterns

// With allowNamedExports: false - INCORRECT
export { foo };
const foo = 1;

// CORRECT
const foo = 1;
export { foo };

// Or use default export
export default foo; // Must be after declaration

When Not to Use It

Disable this rule if:
  1. You’re comfortable with hoisting and temporal dead zones
  2. Your codebase heavily uses forward references
  3. You need mutual recursion with function declarations
For most projects, this rule helps catch bugs and improves code clarity.