Learn / Blog / Article
Temporal Dead Zone: the lesser-known part (and how to avoid it)
Last updated9 Aug 2023
Reading time4 min
What is a Temporal Dead Zone?
According to MDN, "A let or const variable is said to be in a ‘Temporal Dead Zone’ from the start of the block until code execution reaches the line where the variable is declared and initialized."
Okay, this is obvious—you cannot access a constant before you declare it. But nobody writes code like this!, you say. Moreover, ESLint will yell at you if you try. So what’s the big deal?
But, again, nobody writes code like this, right? And ESLint is smart enough to warn you if you try. What’s your problem, dude?
When ESLint can’t help you
ESLint analyzes the code statically. It’s smart, but because it doesn’t understand all the nuances of the runtime flow, a code like the one above is perfectly valid for the checker.
A real-life example
It all seems a bit made-up: do we use patterns like this in our codebase? It turns out they’re rare, but they’re there. And they look completely innocent.
Take a look at what I found in Hotjar’s codebase:
This code may feel illogical, but the subscribe method is a bit more complex than the callCallback you could see above. It calls the callback asynchronously most of the time, but in certain cases, it does it synchronously. And this leads to a runtime error about an uninitialized variable when you try to unsubscribe inside the callback.
But I’ve never encountered such an error
How could a code like this even slip into the main branch? It’s broken, and loading it in the browser or running a test would detect it immediately, right? It would—if we used const and let bindings. But for a long time, this wasn’t the case at Hotjar, and we didn’t realize it!
Despite this syntax having been the norm here for years now, with several such bindings present in our codebase, so far, all of them have been transpiled to var before they reached the browser. For these, hoisting mechanism kicks in and silently evaluates the uninitialized binding to undefined.
At Hotjar, the transpilation step was there because we were supporting IE 11 and other ‘odd, old browsers’ that didn’t understand the const and let syntax. It was replaced with var on CI (via Babel), in a way engineers working in product squads weren’t noticing. Nobody could see errors like the one above when testing the application.
Things changed, though. Last year, we officially dropped support for IE 11 in our product, which opened possibilities for dropping some compilation steps. It not only made our CI and development environment faster, but also had benefits for customers. ES6+ code is more terse, thus takes less time to load. The side-effect is that consts and lets are directly executed in the browser. So now, Hotjar engineers can, and certainly will, occasionally spot these errors. They’ve always been there, but they've been hidden.
How to detect TDZ access
In the example above, failing E2E (we use Cypress) tests for some areas of our codebase uncovered the issue. This was great, but also strange (and a bit concerning) at the same time: no other check—linter, type check, or unit tests—failed. This proves that some mistakes can be only spotted in runtime. Different test types are complementary and cannot replace each other.
How to prevent TDZ access
Use let, whenever there is a risk of accessing a binding before its initialization ends.
The code is now even clearer and more explicit. Looking at the original version, you might not understand why we need to check if the unsubscribe isn’t empty before calling it. After all, its type, according to TypeScript, would always be () => void, no? But as we’ve proven, this wasn’t true. After switching to let, it’s obvious that, in some particular moment, this name could contain null.
Watch out for Temporal Dead Zones, even if ESLint doesn’t warn you. Write tests, including E2E ones. And, make sure you’re stayin' alive.
Hotjar's tech blog
The power and pitfalls of regular expressions
‘Put our customers at the heart of everything’ is Hotjar's foremost core value. In line with this principle, our top priorities are protecting and being consistently available to our users. Regular expressions, or regex, are commonly employed to safeguard web services against harmful inputs. But they can also (somewhat ironically) become a source of vulnerability if not meticulously constructed.
Hotjar's tech blog
How we optimized perceived performance to improve our KPIs: a Hotjar case study
No one likes to wait. Even at a nice restaurant, if the food takes too long to arrive, you’ll start wriggling on your chair and looking around for the waiter. Worst case, you might even leave.
This is nothing new: people are impatient by nature, and your users are no different. If you ask them to wait too long for their requested content, they’ll enjoy the experience less and eventually drop off.
Hotjar's tech blog
Observability for product teams: what, why, who, and how
Observability is not a new concept in the software industry, but it still amazes me how many different interpretations I hear about this term. In many cases, observability ownership appears ambiguous, and it's also common to see it treated as an afterthought.