no-use-before-define

Disallow using a variable, function, or class above the line that declares it

Reading a let, const, or class before its declaration runs throws a ReferenceError. Reading a var early gives undefined without any error. Function declarations do work before their line, but a reader has to scroll down to find out what's being called.

The rule goes by position in the file. A function that mentions a variable declared below it is reported, even if the function only runs later. The options relax that.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"no-use-before-define" = "error" # or "warn"

Every option, set to its default:

bunfig.toml
[lint.rules]
"no-use-before-define" = ["error", {
  functions = true,
  classes = true,
  variables = true,
  allowNamedExports = false,
  enums = true,
  typedefs = true,
  ignoreTypeReferences = true,
}]

Examples#

example.js3 problems
console.log(limit);
const limit = 10;

start();
function start() {}

const total = total + 1;
example.js1 problem
function report() {
  return limit;
}

const limit = 10;
example.jsNo problems
const limit = 10;
console.log(limit);

function start() {}
start();

Options#

OptionDefaultWhat it does
functionstrueReport function declarations that are used early.
classestrueWith false, a function may mention a class that's declared below it. An early use in the same scope is still reported.
variablestrueThe same as classes, for var, let, and const.
allowNamedExportsfalseAllow export { name } above the declaration of name.
enumstrueReport TypeScript enums that are used early.
typedefstrueReport type aliases and interfaces that are used early.
ignoreTypeReferencestrueAllow early uses in a type position, such as let user: User or typeof config.

The string "nofunc" is short for { "functions": false }.

With functions: false, you can call a function declaration early. It's already defined when the file starts to run:

example.jswith { "functions": false }No problems
start();

function start() {}

With variables: false, a function may mention a variable that's declared below it:

example.jswith { "variables": false }No problems
function report() {
  return limit;
}

const limit = 10;

An early use in the same scope runs before the declaration, so it's still reported:

example.jswith { "variables": false }1 problem
console.log(limit);
const limit = 10;

With allowNamedExports: true, you can keep the list of exports at the top of the file:

example.jswith { "allowNamedExports": true }No problems
export { limit };

const limit = 10;

Types don't exist when the code runs, so by default you can name a type before it's declared:

example.tsNo problems
let current: User;

interface User {
  name: string;
}

With ignoreTypeReferences: false, the same code is reported:

example.tswith { "ignoreTypeReferences": false }1 problem
let current: User;

interface User {
  name: string;
}