Description
π Search Terms
globalThis, declare global block, let, const, var, property
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
This wouldn't be a breaking change in existing JavaScript code. When it comes to TypeScript code, I'm not as sure...
Before my request, here's an excerpt of #30477 for context:
TypeScript Version: 3.4.0-dev.20190316
Search Terms: 3.4 globalThis let const property
Code
// Compile with `tsc -t es2015` const foo: number = 42; const bar: null = globalThis.foo; // Type 'number' is not assignable to type 'null';For reference, the following demonstrates how these bindings behave in browser and node, respectively:
<script> const foo = 42; alert(window.foo); // --> "undefined" </script>
const foo = 42; console.log(global.foo); // --> "undefined"Expected behavior: TypeScript models types consistently with what's actually happening in VMs. Specifically, variables bound by
let
orconst
are not translated to properties onglobalThis
.Actual behavior: TypeScript converts
let
/const
bindings onto properties ofglobalThis
, but these properties do not exist in ES2015+ output (whenlet
andconst
are retained in the output).
The example and fix makes sense to me and I'm not requesting a change for that. But is there any chance #30510 / #30477 could work the way it used to within declare global
blocks? In other words, I'm requesting for all of this code to compile:
export {}
declare global {
const c: string;
let l: string;
var v: string;
}
globalThis.c = "s"; // errors: Property 'c' does not exist on type 'typeof globalThis'.
globalThis.l = "s"; // errors: Property 'l' does not exist on type 'typeof globalThis'.
globalThis.v = "s"; // compiles
π Motivating Example
The examples in the #30477 excerpt make sense to me because they're analogous to runtime behavior. e.g., I can intuit const foo: number = 42;
shouldn't exist on globalThis
.
But declare global { ... }
doesn't exist at runtime in at all. It's a part of TypeScript that's erased at transpile. So if this is TypeScript-only, and it's named "declare global," I would assume my const
s and let
s apply to global scope.
Well it does.
Sometimes.
As a newb, it feels kind of arbitrary. For example, this code compiles like I would expect:
export {}
declare global {
interface Window {
foo: number;
}
function Y(): void;
}
globalThis.window.foo;
globalThis.Y();
And if I remove globalThis
from my first example, it behaves the way I would expect:
export {}
declare global {
const c: string;
let l: string;
var v: string;
}
c = "s"; // Cannot assign to 'c' because it is a constant.
l = "s"; // compiles
v = "s"; // compiles
So, it's conditionally global?
This stack overflow post shows some conversation around this topic..
π» Use Cases
- What do you want to use this for? I want it to be easier to learn how to declare global types.
- What shortcomings exist with current approaches? I think the current approach is nuanced and behaves in unexpected ways.
- What workarounds are you using in the meantime? I refer back to these links whenever global variable declarations don't work the way I expect