Declaring and Managing Global Variables in TypeScript

January, 7th 2025 4 min read

Global variables have always existed in JavaScript, but when projects grow, unstructured global state quickly becomes a source of confusion. TypeScript attempts to bring clarity to this area by allowing developers to declare globals explicitly, type them, extend existing global interfaces, and control how the rest of the codebase interacts with them.

This article provides a detailed, practical explanation of how global variables work in TypeScript, how to declare them safely, how to avoid conflicts, and how to integrate them into real-world applications without turning the global scope into an undocumented set of magic values.


1. What a “Global Variable” Means in TypeScript

In JavaScript, a global variable is any value attached to the global execution context, such as:

  • window in browsers
  • global in Node.js
  • globalThis in modern environments

TypeScript does not change how these globals work. Instead, it adds a type layer so that developers can:

  • explicitly declare globals
  • document their structure
  • ensure type checking across the project
  • prevent unexpected runtime errors

Because of this, correct global variable declaration becomes a matter of defining types properly rather than simply assigning values.


2. Creating a Global Declaration File

The most reliable way to introduce a global variable is to declare it in a .d.ts file. This file should not contain executable code. Its purpose is to tell TypeScript: “this value exists at runtime; here is its type.”

Create a file such as:

plaintext
globals.d.ts

Inside it, declare the variable:

ts
declare const API_URL: string;

With this, TypeScript recognizes the variable across the project. If the variable is missing at runtime, TypeScript will not catch it—declarations describe shape, not execution—but the compiler will enforce correct usage in code.


3. Assigning Global Variables at Runtime

Declaration does not create the variable. You must assign it yourself.

Example in HTML

html
<script>
  window.API_URL = "https://api.example.com";
</script>

Example in TypeScript or Node.js

ts
(globalThis as any).API_URL = "https://api.example.com";

Using globalThis makes the code environment‑agnostic, working the same way in Node, browsers, and workers.


4. Accessing Global Variables in TypeScript

Once the declaration file exists, TypeScript allows direct access:

ts
console.log(API_URL);

If you access a global not declared anywhere, the compiler reports an error. This prevents accidental creation of undeclared globals.


5. Extending Existing Global Interfaces

Sometimes the global variable belongs to window, global, or globalThis. In these cases, extend the appropriate interface.

Extending window:

ts
// globals.d.ts
interface Window {
  API_URL: string;
}

Assign it:

ts
window.API_URL = "https://api.example.com";

Use it:

ts
console.log(window.API_URL);

This approach is useful when the variable is tied to browser environments, configuration injected at build time, or integrations with third‑party scripts.


6. Organizing Globals With Namespaces

If an application requires multiple related global values, placing them directly on the global scope can cause clutter or naming collisions. Grouping them inside a namespace helps keep structure predictable.

ts
declare namespace AppConfig {
  const API_URL: string;
  const VERSION: string;
  const ENABLE_LOGS: boolean;
}

Runtime assignment:

js
globalThis.AppConfig = {
  API_URL: "https://api.example.com",
  VERSION: "1.2.0",
  ENABLE_LOGS: true
};

Access:

ts
console.log(AppConfig.API_URL);

This keeps the global scope cleaner and prevents typos from silently creating new global properties.


7. Managing Environment Variables Instead of Manual Globals

Many projects prefer environment variables instead of manual globals. They reduce the need to hardcode values and integrate naturally with build tools.

Node.js

With dotenv:

plaintext
API_URL=https://api.example.com

Access:

ts
process.env.API_URL;

Frontend builds

Tools like Vite, Next.js, Webpack, and Parcel support environment variable injection during bundling. For example, Vite exposes:

ts
import.meta.env.VITE_API_URL;

Environment variables are not technically globals, but they solve many of the same needs with better separation.


8. Real‑World Use Cases

Build‑time configuration

Static API endpoints, version metadata, feature flags injected during bundling.

Integration with third‑party scripts

SDKs that expose global objects, such as analytics libraries or payment providers.

Providing shared utilities in non‑module scripts

Legacy codebases still relying on script tags rather than module systems.

Bootstrapping runtime‑dependent values

Values that depend on environment detection or dynamic initialization.


9. Common Mistakes and How to Avoid Them

Mistake: Forgetting to declare the global

This leads to any typing or runtime crashes. Always declare globals in .d.ts.

Mistake: Creating accidental globals

For example:

js
API_URL = "value"; // becomes a global by accident

Always use explicit assignment to window/globalThis.

Mistake: Mixing runtime assignment with type declaration

Declarations and runtime values belong in different files.

Mistake: Using globals for everything

Overuse leads to unpredictable dependencies. Prefer modules or environment variables.


Conclusion

Global variables in TypeScript are safe and maintainable when declared properly. By using declaration files, extending global interfaces only when necessary, grouping related values, and relying on environment variables where appropriate, developers can avoid the pitfalls that global state usually introduces.

TypeScript’s approach does not eliminate global variables—it gives you a systematic, well‑typed way to manage them so that large applications remain predictable and organized.