ReferenceError: Cannot access before initialization (TDZ) — Fixed
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.