TypeScript: Use case of the interface merge declaration
Imagine that we want to extend the Window
from anywhere in our code, making global flags, functions, APIs,… available anywhere.
How would we do that?
It’s quite simple. To do so, we need to declare the Window interface inside a global namespace.
declare global {
interface Window {
…
}
}
The namespaces are used, mostly, when we want to spread type declarations across files. But, whenever we want to extend the Window interface we don’t want to end up overwriting the type entirety, so we will attach a custom field to it.
declare global {
interface Window {
debug: boolean;
}
}
Wait, wait, wait… but tell me where I should declare that kind of thing…
The most common approach is to create an ambient type declaration file (ambient.d.ts
). An ambient type declaration file
allows us to tell TypeScript that some variables, functions,… are globally known and defined. Usually, these local declarations
are stored in a @types
folder at the root of the project.
You may have done something like this: npm install @types/react
With that command you are installing the React type declarations. These declarations are “ambiental” and TypeScript knows how to work with them.
How does TypeScript know that these declarations exist?
In the tsconfig.json
file, we can define a property named typeRoots
. In this property, we define where additional type
information can be found.
{
"compilerOptions": {
"target": "ES2020",
"module": "es2020",
"allowJs": true,
"checkJs": true,
"typeRoots": [
"@types",
"./node_modules/@types"
],
"esModuleInterop": true,
},
"include": ["src", "@types"]
}
In the example above, @types
is our local types folder where we will keep our ambient module declarations. The other is node_modules/@types
where we
get the ambient declaration of the libraries/dependencies that we have installed in the project (for example: @types/react
).
With that setup, we will become debug
available in all our JavaScript/TypeScript files. Assuming that this variable is defined and available in the global scope.
if (window.debug) {
console.log(“Debug mode!”)
}