Diego Betto's Blog
Photo by MJ Duford on Unsplash

Diego Betto · September 10, 2026 · 4 min di lettura

"Cannot access before initialization": the Temporal Dead Zone explained

Why let and const throw ReferenceError instead of returning undefined like var, what the Temporal Dead Zone actually is, and the patterns that trigger it by accident.

Condividi:XLinkedInFacebookWhatsApp

You reference a variable that’s clearly declared further down in the same scope, expecting the usual JavaScript looseness, and instead get:

⛔ Error

ReferenceError: Cannot access ‘config’ before initialization

If you came from var, or from a language without this exact rule, it’s surprising — var would have just given you undefined instead of throwing. The difference is the Temporal Dead Zone, and once you understand it, this error stops being confusing.

var hoisting vs. let/const hoisting

All three — var, let, and const — are hoisted: the engine knows about the variable’s existence from the top of its scope. The difference is what happens between the top of the scope and the actual declaration line.

console.log(x); // undefined — var is hoisted AND initialized to undefined
var x = 5;
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;

var is hoisted and initialized to undefined immediately. let and const are hoisted but left uninitialized until execution actually reaches their declaration. The stretch of code between the top of the scope and that declaration — where the variable exists but touching it throws — is the Temporal Dead Zone (TDZ).

Why the TDZ exists

This isn’t an arbitrary restriction — it catches a real class of bug. With var, reading a variable before its declaration silently gives you undefined, which often masks a logic error (you meant to use it after it was set, but a typo or a refactor moved the read earlier). The TDZ turns that silent undefined into a loud, immediate error at exactly the point you tried to use the not-yet-initialized value.

Where this shows up in real code

Accidentally shadowing an outer variable

let value = "outer";

function example() {
  console.log(value); // throws — not "outer"!
  let value = "inner";
}

example();

It’s easy to assume console.log(value) reads the outer value, since that’s what would happen with var. It doesn’t — let value inside example is hoisted to the top of the function, shadowing the outer variable for the entire function body, including the line before its own declaration. That line is in the TDZ for the inner value, so it throws instead of silently reading the outer one.

A self-referencing default parameter

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

greet(); // ReferenceError: Cannot access 'greeting' before initialization

Default parameters are evaluated left to right, and each one is in the TDZ until its own default has been assigned — name’s default tries to read greeting before greeting has been initialized. Reordering the parameters fixes it.

class declarations are in the TDZ too

new Vehicle(); // ReferenceError — classes are hoisted but stay in the TDZ
class Vehicle {}

This one surprises people coming from other object-oriented languages where class declarations are typically available throughout the file. In JavaScript, class behaves like let/const for this purpose, not like a function declaration (which is fully hoisted and callable before its textual position).

var vs let/const: a direct comparison

var let / const
Hoisted Yes Yes
Initialized at hoist time Yes, to undefined No — stays in the TDZ
Accessing before declaration Returns undefined Throws ReferenceError
Scope Function-scoped Block-scoped

FAQ

❓ Is the TDZ a JavaScript bug or a bug in my code?

Neither, exactly — it’s the language deliberately failing loudly instead of silently returning undefined. If you’re hitting it, there’s almost always a real ordering issue in your code worth fixing (a shadowed variable, a misordered default parameter), not something to work around.

❓ Does the TDZ apply to function parameters too?

Yes — parameters are essentially let-scoped within the function, which is exactly why the self-referencing default parameter example above throws.

❓ Can typeof avoid triggering the TDZ, like it does for undeclared variables?

No, and this is a subtle but important exception: typeof someUndeclaredVar normally returns "undefined" safely for a variable that was never declared at all, but typeof on a let/ const variable that’s still in its TDZ throws the same ReferenceError — the TDZ makes even typeof unsafe for that specific variable.

Conclusion

The Temporal Dead Zone is JavaScript converting a category of silent bug — reading a var before its intended assignment and getting undefined without any warning — into a loud, immediate ReferenceError for let, const, and class. The three patterns above (accidental shadowing, self-referencing default parameters, and early class references) cover almost every real-world case; once you recognize the shape, the fix is usually just reordering the code.

References

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Written by

Diego Betto

Co-Founder & CTO at PAPION. Senior full-stack engineer specializing in React, TypeScript, Node.js, and application security.