Extend the other answer about globalThis
(see MDN and TypeScript 3.4 note) with more specific examples (TypeScript only without mixing with JavaScript), as the behavior was fairly confusing. All examples are run under Nodejs v12.14.1
and TypeScript Version 4.2.3
.
Simplest case with global scope
declare var ENVIRONMENT: string;
globalThis.ENVIRONMENT = 'PROD';
console.log(ENVIRONMENT);
console.log(globalThis.ENVIRONMENT);
// output
// PROD
// PROD
This file doesn't import
or export
so it's a global scope file. You can compile the above TypeScript code without any error. Note that you have to use var
. Using let
will throw error TS2339: Property 'ENVIRONMENT' does not exist on type 'typeof globalThis'.
You might notice that we declare
d the variable as opposed to the following which also works.
var ENVIRONMENT: string;
ENVIRONMENT = 'DEV';
globalThis.ENVIRONMENT = 'PROD';
console.log(ENVIRONMENT);
console.log(globalThis.ENVIRONMENT);
// output
// DEV
// PROD
The output is from Nodejs v12.14.1
. I also tested it in Chrome (after compiling to JS) and both output PROD
. So I'd suggest using globalThis
all the time.
Simple case with module scope
declare var ENVIRONMENT: string;
globalThis.ENVIRONMENT = 'PROD';
export {};
Once we add export
statement, it becomes a module scope file, which throws error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
The solution is to augment global scope.
declare global {
var ENVIRONMENT: string;
}
globalThis.ENVIRONMENT = 'PROD';
console.log(globalThis.ENVIRONMENT);
export {};
You still have to use var
, otherwise you will get error TS2339: Property 'ENVIRONMENT' does not exist on type 'typeof globalThis'.
.
Import for side effect
// ./main.ts
import './environment_prod';
console.log(ENVIRONMENT);
console.log(globalThis.ENVIRONMENT);
// ./environment_prod.ts
declare var ENVIRONMENT: string;
globalThis.ENVIRONMENT = 'PROD';
Or
// ./environment_prod.ts
declare global {
var ENVIRONMENT: string;
}
globalThis.ENVIRONMENT = 'PROD';
export {}; // Makes the current file a module.
Browserify two files
Suppose both main.ts
and environment_prod.ts
are entry files. Browserify will wrap them (after compiled to JS) into local scoped functions which necessitates the use of globalThis
.
// ./main.ts
declare var ENVIRONMENT: string;
console.log(ENVIRONMENT);
console.log(globalThis.ENVIRONMENT);
// ./environment_prod.ts
declare var ENVIRONMENT: string;
globalThis.ENVIRONMENT = 'PROD';
But it's more type-safe to share a declaration file which can then be imported by both entry files, to avoid typos of variable names or type names.
// ./main.ts
import './environment';
console.log(ENVIRONMENT);
console.log(globalThis.ENVIRONMENT);
// ./environment_prod.ts
import './environment';
globalThis.ENVIRONMENT = 'PROD';
// ./environment.ts
type Environment = 'PROD' | 'DEV' | 'LOCAL';
declare var ENVIRONMENT: Environment;
Note that the order matters: browserify environment_prod.js main.js > bin.js