What are global types/interfaces in TypeScript?
Tags: typescript, javascript, programming
Published: 2023-05-09 03:03:16

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:
```ts
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:

![The screenshot of the Window interface declaration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gb59bna57ig04ajmtmpt.png)

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:

```ts
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
```

```ts
// 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

![Output on the screen that shows an API key and API URL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w3cuyyfa8grx3s2mclua.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slykj69mmfjwrshlcj30.png)

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:

```ts
// 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.

![Typing process.env. and get enough suggestions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ev2j835dyujpegxhw87w.png)

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](https://www.typescriptlang.org/docs/handbook/namespaces.html).

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](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures) if you are not familiar with it.

![ProcessEnv interface declaration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpjipkwv88frmxrdzaxs.png)

## 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.