Error when accessing properties on `globalThis` with the same name of a global variable
🔎 Search Terms
globalThis redefined error script property does not exist
🕗 Version & Regression Information
- This is the behavior in every version I tried
⏯ Playground Link
https://www.typescriptlang.org/play/?noImplicitAny=false&ts=5.5.0-beta#code/DYUwLgBAlgdgbgQ2FAJgRggXggc2AewCMkAVACygGcA6WRZdAbgChRIBPLXA44cq2vCSo0LNhABeXPEVIUadYSgBMLIA
💻 Code
let invalid1 = globalThis.invalid1;
let y = globalThis.invalid1;
let z = globalThis.invalid2;
🙁 Actual behavior
Property 'invalid1' does not exist on type 'typeof globalThis'.
🙂 Expected behavior
It should either complain about both invalid1 and invalid2, or neither
Additional information about the issue
It only happens in scripts and not in modules (if I add export {}, there are no reported errors).
I think what you should actually be seeing here is a circularity error
let invalid1 = 1 doesn't define globalThis.invalid1, so there is no circularity going on (it only happens with var).
Note that the code above has actually valid use cases. I spotted it while doing
let SuppressedError = globalThis.SuppressedError ?? class SuppressedError extends Error {
// ...
}
Hello @nicolo-ribaudo.
Attempting to use the variable before declaration will result in a ReferenceError. This is known as the "temporal dead zone," where the variable exists in the scope but cannot be accessed.
But, When you declare a variable with var, the variable declaration is "hoisted" (or "raised") to the top of your current scope. This means that, regardless of where you declared the variable in the code, JavaScript treats it as if you had declared it at the beginning of the scope. However, only the declaration is hoisted, not the initialization. Therefore, if you try to access the variable before initializing it, the value will be undefined, but it will not cause an error.
On the other hand, when you declare a variable with let, the declaration is also hoisted, but JavaScript puts these variables in a "temporal dead zone" from the start of the block until the declaration is processed. During this "dead zone", if you try to access the variable, you will receive a ReferenceError.
So, in your case, when you try to do let invalid1 = globalThis.invalid1; without globalThis.invalid1 being defined, invalid1 is undefined. This is not a problem in itself, but if you try to access invalid1 somewhere in the code before this line, you will receive a ReferenceError because of the "temporal dead zone" of let.
However, if you use var instead of let, as in var invalid1 = globalThis.invalid1;, even if globalThis.invalid1 is not defined, invalid1 will simply be undefined and will not cause an error, even if you access invalid1 before this line, thanks to the hoisting of var.
In the example above no variables are in temporal dead zone -- you can try running that code and it does not error.
FWIW, this error is reported here and this is the mergeSymbols call that is responsible for putting the invalid1 member onto that symbol table.
In a sense, this is by design as the code is somewhat explicit about it. But perhaps this should be revisited.