Declaring Global Variables in TypeScript
Every now and then, you might want to statically type a global variable in TypeScript. For example, in some of my web applications, I need to pass a few properties from my markup rendered on the server to my JavaScript code running in the browser. To do that, I typically define a global variable named __INITIAL_DATA__
within an inline script and assign to it a JSON-serialized object:
<script>
window.__INITIAL_DATA__ = {
userID: "536891193569405430",
};
</script>
Now, if I try to access window.__INITIAL_DATA__
in a TypeScript file, the compiler will produce a type error because it can't find a definition of the __INITIAL_DATA__
property anywhere:
// Property '__INITIAL_DATA__' does not exist
// on type 'Window & typeof globalThis'
const initialData = window.__INITIAL_DATA__;
I'm going to show you a few different approaches for letting TypeScript know about the window.__INITIAL_DATA__
property and making the type error go away.
#Using a Type Assertion
The quickest way to make the type error go away is to use the any
type in a type assertion. We can treat the window
object to be of type any
so that we can access its __INITIAL_DATA__
property:
const initialData = (window as any).__INITIAL_DATA__;
This solution works, and we longer get a type error. This is a pragmatic approach if you need an ad-hoc way to access a property on the window
object that TypeScript doesn't know about.
The (window as any).__INITIAL_DATA__
expression is of type any
, and therefore initialData
is of type any
too. We could go one step further and use another type assertion to give the initialData
variable a more specific type:
type InitialData = {
userID: string;
};
const initialData = (window as any).__INITIAL_DATA__ as InitialData;
Now, we can access initialData.userID
in a type-safe way:
const userID = initialData.userID; // Type string
Do keep in mind that this is not a guarantee that window.__INITIAL_DATA__
will be set correctly at runtime. The type checker trusts us and it is our job to make sure that we assign an object with the expected shape to window.__INITIAL_DATA__
.
#Declare a Global Variable
Another approach is to declare a global variable using the declare var
syntax. This way, we can let TypeScript know that it can expect to find a global variable with the given name and type:
declare var __INITIAL_DATA__: InitialData;
We can now access the __INITIAL_DATA__
variable directly …
const initialData = __INITIAL_DATA__;
… or off of the window
object:
const initialData = window.__INITIAL_DATA__;
Note that the access via window.__INITIAL_DATA__
will not work from within an ECMAScript module. If your JavaScript file contains top-level import
or export
declarations, it is considered a module, and you will receive a type error if you try to access the __INITIAL_DATA__
on the window
object.
You can declare a global variable in the global scope by using the declare global { ... }
syntax to be able to access both window.__INITIAL_DATA__
as well as __INITIAL_DATA__
directly within a JavaScript module.:
export function someExportedFunction() {
// ...
}
declare global {
var __INITIAL_DATA__: InitialData;
}
const initialData = window.__INITIAL_DATA__;
If you need to access window.__INITIAL_DATA__
in several files or modules, it might be a good idea to create a globals.d.ts file in your project. In that file, you can declare all global variables you'll use:
declare var __INITIAL_DATA__: InitialData;
As long as globals.d.ts is part of your TypeScript project, the compiler will know that __INITIAL_DATA__
is a global variable, and it will let you access it via both __INITIAL_DATA__
as well as window.__INITIAL_DATA__
.
#Augmenting the Window Interface
Lastly, you can use TypeScript's interface declaration merging to let the compiler know that it can expect to find a property named __INITIAL_DATA__
on the Window
type and therefore the window
object. To do that, you'll need to define an interface named Window
with a property named __INITIAL_DATA__
:
interface Window {
__INITIAL_DATA__: InitialData;
}
TypeScript will merge this interface definition together with the Window
interface defined in lib.dom.d.ts, resulting in a single Window
type. Now, the following assignment will no longer produce a type error:
const initialData = window.__INITIAL_DATA__;
Note that once again, this approach will not work within a JavaScript module. You'll need to use the declare global { ... }
syntax again in order for the window.__INITIAL_DATA__
expression to type-check correctly:
export function someExportedFunction() {
// ...
}
declare global {
interface Window {
__INITIAL_DATA__: InitialData;
}
}
const initialData = window.__INITIAL_DATA__;