Do you know what global types/interfaces are?
In TypeScript, we can use types/interfaces declared in module files by importing and exporting them. However, in addition to that, we can use types/interfaces floating around in the TypeScript type system.
Global Objects
When you hover a cursor over the window
object, you can see the type below:
var window: Window & typeof globalThis
As you can see, the type (interface) of the window
object is Window
. You will be able to observe the type even though you have never declared the type. So what is going on behind the scene?
If you are working on a project with TypeScript, please dive into the node_module package of typescript
. You will be able to find the Window
interface declaration in lib.dom.d.ts like the image below:
TypeScript prepares the global types/interfaces for the global objects such as window
and localStorage
. Thanks to that, we can use the types of these global objects without our own declaration.
Declare Global Types/Interfaces
If you want to declare types/interfaces globally, it is possible to do that using some techniques. Let me explain how to do it.
Make 'scripts' instead of 'modules'
In TypeScript and modern JavaScript projects, using modules rather than scripts is generally recommended because modules are easier to maintain. But we can take advantage of the global scripts to make global types/interfaces. If you want to make a script file, do not use any import
or export
keywords in the file. As long as you do not use the keywords, the file is treated as a script file by TypeScript, and types/interfaces in the file are accessible in the global scope.
'declare global block'
You can even declare global types/interfaces within the declare global
block in the module file. Here is an example:
export type ThisIsAType = string;
declare global {
type ThisIsAGlobalType = string;
}
If you want to use ThisIsAType
in a different file, you have to import it. On the other hand, you do not have to import ThisIsAGlobalType
wherever you want to use it in your project because it is floating in the global scope.
Example in the Real-Scenario
As you know, there are several objects you can access in the global scope. This time, I will give you one example of utilizing this technique for environment variables in a React.js project.
When you want to use environment variables in React.js, you will make a .env file in the project's root and access the env vars using the process.env
. The code will be like below:
// .env
REACT_APP_API_KEY=1234567890abcdef
REACT_APP_API_URL=https://api.example.com
// App.tsx
function App() {
const apiKey = process.env.REACT_APP_API_KEY;
const apiUrl = process.env.REACT_APP_API_URL;
return (
<div>
<p>API key: {apiKey}</p>
<p>API URL: {apiUrl}</p>
</div>
);
}
export default App;
Output on the screen
It works with this minimum setup though we can not use an autocomplete feature of the IDE. I mean, even if you type process.env.
, you can not find REACT_APP_API_KEY
and REACT_APP_API_URL
in a suggestion, like an image below:
If there are a lot of env vars, you may make a typo of the env vars' names. To reduce the risk of typos or something, it may be good to merge your custom interface to add properties to the ProcessEnv
interface by using the global scope declaration. Here is an example:
// types.d.ts
declare global {
namespace NodeJS {
interface ProcessEnv {
REACT_APP_API_KEY: string | undefined;
REACT_APP_API_URL: string | undefined;
}
}
}
export {};
After making the type declaration file and writing the code above, you can find your env vars' names, REACT_APP_API_KEY
and REACT_APP_API_URL
, in a suggestion.
Perfect! We could add the custom properties to the ProcessEnv
interface.
Note 1: A namespace
is a way to group related code and prevent naming collisions. This may be helpful when you are working on a large-scale project or making a node_module. You can find more info in the official doc.
Note 2: The TypeScript type system cannot warn you even when trying to access a property (environment variable) that does not exist in process.env
because the ProcessEnv
interface has an index signature. Please refer to the official doc if you are not familiar with it.
Conclusion
TypeScript's global types and interfaces allow seamless access to global objects like window
and localStorage
. Declaring global types can be done by creating 'scripts' or using the 'declare global' block. These techniques enhance the developer experience, such as providing autocompletion suggestions for environment variables.
Overall, global types and interfaces in TypeScript contribute to more maintainable code and an improved developer experience.
Top comments (0)