DEV Community

Cover image for What are global types/interfaces in TypeScript?
Tomohiro Yoshida
Tomohiro Yoshida

Posted on • Updated on

What are global types/interfaces in TypeScript?

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
Enter fullscreen mode Exit fullscreen mode

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:

The screenshot of the Window interface declaration

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
// 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;
Enter fullscreen mode Exit fullscreen mode

Output on the screen

Output on the screen that shows an API key and API URL

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:

Typing process.env. but there are not enough suggestions

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 {};
Enter fullscreen mode Exit fullscreen mode

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.

Typing process.env. and get enough suggestions

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.

ProcessEnv interface declaration

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)