JavaScript Errors

ReferenceError: Cannot access before initialization (TDZ) — Fixed

ReferenceError: Cannot access before initialization — Temporal Dead Zone

What Does This Error Mean?

JavaScript raises ReferenceError: Cannot access '<variable>' before initialization when you read a variable declared with let or const before the engine has executed the line where it is declared. This region — between the start of the scope and the declaration line — is called the Temporal Dead Zone (TDZ). The variable exists in scope (it was hoisted) but it is not yet initialized, so every read throws.

Common Causes (With Code)

1. Using a let variable before its declaration

Unlike var, which is hoisted and initialized to undefined, let and const are hoisted but not initialized. Touching them before the declaration line is a guaranteed TDZ crash.

❌ Causes the error

console.log(username); // ReferenceError: Cannot access 'username' before initialization
let username = "Alice";

✅ With var — works but is misleading (avoid)

console.log(username); // undefined  (not an error, but confusing)
var username = "Alice";

2. Calling a function that references a const before it's initialized

If a function closure captures a const and is invoked before that const is set, the TDZ error propagates through the call.

❌ Causes the error

function greet() {
  return `Hello, ${name}`; // `name` is in TDZ when greet() runs
}

console.log(greet());       // ReferenceError: Cannot access 'name' before initialization
const name = "Bob";

3. Class declarations — classes are also in the TDZ

Classes declared with class behave like let. Instantiating them before the declaration throws the same error.

❌ Causes the error

const dog = new Animal("Rex"); // ReferenceError
class Animal {
  constructor(name) { this.name = name; }
}

How to Fix It

Fix 1 — Always declare before use

The rule is universal: move every let, const, or class declaration to the top of its scope, above the first reference to it.

✅ Correct

const name = "Bob"; // ← declared first

function greet() {
  return `Hello, ${name}`;
}

console.log(greet()); // "Hello, Bob"

Fix 2 — Move class declarations before instantiation

✅ Correct

class Animal {
  constructor(name) { this.name = name; }
}

const dog = new Animal("Rex"); // works fine
console.log(dog.name); // "Rex"

Fix 3 — Use function declarations (not expressions) for hoisting when needed

Traditional function declarations are fully hoisted — both the name and the body. This lets you call them before the declaration when you consciously want that behaviour.

✅ Correct — function declarations are hoisted

console.log(add(2, 3)); // 5 — works because function declarations are hoisted

function add(a, b) {
  return a + b;
}

❌ Function expressions are NOT hoisted

console.log(add(2, 3)); // ReferenceError — const is in TDZ
const add = (a, b) => a + b;

Frequently Asked Questions

Does the TDZ only affect let and const?

Yes. The Temporal Dead Zone applies exclusively to let, const, and class. Variables declared with var are initialized to undefined at hoist time, so reading them before the assignment returns undefined instead of throwing.

Why do let and const have a TDZ if they're hoisted?

By design. The spec authors intentionally made them fail loudly when read too early. Accessing a var before its value is assigned silently returns undefined, hiding bugs. The TDZ forces you to write code in execution order, making programs easier to reason about.

Can a linter catch TDZ errors before runtime?

Yes. ESLint's no-use-before-define rule flags exactly this pattern. Enable it in your .eslintrc with "no-use-before-define": "error" to catch TDZ issues at development time instead of in production.