Question
In TypeScript, how can you explicitly assign a custom property on the window object without getting a type error?
For example, suppose you want to create a global namespace like this:
window.MyNamespace = window.MyNamespace || {};
TypeScript highlights MyNamespace and reports an error similar to this:
Property 'MyNamespace' does not exist on type 'Window & typeof globalThis'.
A workaround is to declare MyNamespace as an ambient global variable and then remove the explicit window reference:
declare var MyNamespace: any;
MyNamespace = MyNamespace || {};
However, the goal is to keep the explicit window.MyNamespace form. How can this be done correctly in TypeScript?
Short Answer
By the end of this page, you will understand why TypeScript rejects unknown properties on window, how to extend the Window type correctly, and how to safely attach your own global objects while keeping type checking intact.
Concept
TypeScript does not allow you to access arbitrary properties on built-in objects unless those properties are part of the object's type.
In JavaScript, this is valid:
window.MyNamespace = window.MyNamespace || {};
JavaScript is dynamic, so you can attach new properties to objects at runtime.
TypeScript is static, so it checks whether MyNamespace is declared on the Window interface before allowing that code.
That means the problem is not whether JavaScript can do it. It can. The problem is whether TypeScript knows about it.
To make TypeScript happy, you usually augment the global Window interface so the compiler knows that window.MyNamespace is a valid property.
This matters in real projects because:
- it preserves type safety
- it avoids using
anyeverywhere - it documents global variables clearly
- it helps editors provide autocomplete and error checking
Instead of bypassing the compiler, the better solution is to teach the compiler about your global property.
Mental Model
Think of window as a predefined storage cabinet with a labeled inventory list.
- JavaScript lets you open the cabinet and throw in a new folder named
MyNamespace - TypeScript checks the inventory list first
- if
MyNamespaceis not on the list, TypeScript says: "I don't know that this folder belongs here"
So the fix is not to stop using the cabinet. The fix is to update the inventory list.
In TypeScript, updating that inventory list means extending the Window interface.
Syntax and Examples
The standard solution is to declare the property on Window.
Basic syntax
declare global {
interface Window {
MyNamespace: Record<string, unknown>;
}
}
Then you can use:
window.MyNamespace = window.MyNamespace || {};
Complete example
export {};
declare global {
interface Window {
MyNamespace: {
version?: string;
};
}
}
window.MyNamespace = window.MyNamespace || {};
window.MyNamespace.version = "1.0.0";
console.log(..);
Step by Step Execution
Consider this code:
interface Window {
MyNamespace: { count?: number };
}
window.MyNamespace = window.MyNamespace || {};
window.MyNamespace.count = 1;
Here is what happens step by step:
- TypeScript reads the
Windowinterface extension. - It learns that
windowmay contain a property namedMyNamespace. - The assignment
window.MyNamespace = window.MyNamespace || {}is now valid becauseMyNamespaceis a known property. - If
window.MyNamespacealready exists, it is reused. - If it does not exist yet,
{}is assigned. - The next line sets
countto1.
Runtime view
If window.MyNamespace starts as :
Real World Use Cases
Custom properties on window are common when code needs a browser-wide shared value.
Common examples
-
Third-party script integration
- storing analytics objects
- exposing SDKs loaded by script tags
-
Application bootstrapping
- attaching configuration loaded before the app starts
- storing server-injected settings
-
Legacy code migration
- old JavaScript apps often use
window.App,window.Utils, orwindow.Config - TypeScript can support this safely with interface augmentation
- old JavaScript apps often use
-
Debugging hooks
- exposing a development helper globally for testing in the browser console
Example: runtime config
interface Window {
AppConfig: {
apiBaseUrl: string;
};
}
window.AppConfig = {
apiBaseUrl: "https://api.example.com"
};
fetch();
Real Codebase Usage
In real projects, developers usually avoid creating many globals, but when they do need one, they type it explicitly.
Common patterns
1. Global declaration file
A common setup is a global.d.ts file:
interface Window {
MyNamespace: {
log: (message: string) => void;
};
}
This keeps global type definitions separate from runtime logic.
2. Guarded initialization
window.MyNamespace = window.MyNamespace || {
log(message: string) {
console.log(message);
}
};
This avoids overwriting an existing object.
3. Configuration container
interface Window {
AppSettings: {
environment: | ;
};
}
Common Mistakes
1. Using window without extending Window
Broken code:
window.MyNamespace = {};
Why it fails:
- TypeScript does not know that
MyNamespaceexists.
Fix:
interface Window {
MyNamespace: {};
}
2. Using as any everywhere
Broken approach:
(window as any).MyNamespace = {};
(window as any).MyNamespace.value = 123;
Why it is a problem:
- you lose type checking
- typos will not be caught
- autocomplete becomes weaker
Better:
Comparisons
| Approach | Example | Type Safety | Best Use |
|---|---|---|---|
Extend Window | interface Window { MyNamespace: {} } | High | Best general solution |
Cast to any | (window as any).MyNamespace | Low | Quick temporary workaround |
| Declare global variable | declare var MyNamespace: any | Low to medium | Legacy code that does not need window. |
| Use module/global config object instead | const config = {...} | High | Preferred when a true global is not needed |
Cheat Sheet
Add a custom property to window
In a declaration file
interface Window {
MyNamespace: {
version?: string;
};
}
In code
window.MyNamespace = window.MyNamespace || {};
window.MyNamespace.version = "1.0.0";
Module-style global augmentation
export {};
declare global {
interface Window {
MyNamespace: Record<string, unknown>;
}
}
Quick workaround
(window as any).MyNamespace = {};
Use only when necessary.
FAQ
Why does TypeScript complain about window.MyNamespace?
Because MyNamespace is not part of the built-in Window interface, so TypeScript treats it as an unknown property.
How do I declare a custom property on window in TypeScript?
Extend the Window interface, usually in a .d.ts file:
interface Window {
MyNamespace: {};
}
Can I use (window as any) instead?
Yes, but it disables type safety for that access. It is better to extend Window properly.
Does extending Window create the property at runtime?
No. It only informs the TypeScript compiler. You still need to assign the property in code.
Should I use declare var MyNamespace instead of window.MyNamespace?
Only if you want a direct global variable. If you specifically want a property on window, extend .
Mini Project
Description
Create a small browser app configuration object attached to window. This demonstrates how to safely define and use a custom global property in TypeScript without losing type safety.
Goal
Build a typed window.AppConfig object and read its values from normal application code.
Requirements
- Define a custom
AppConfigproperty on theWindowtype. - Initialize
window.AppConfigif it does not already exist. - Store at least two configuration values.
- Read and print those values in TypeScript code.
Keep learning
Related questions
Angular formGroup Error Explained: Fixing 'Can't bind to formGroup' in Reactive Forms
Learn why Angular shows 'Can't bind to formGroup' and how to fix it by importing ReactiveFormsModule correctly.
Fix "Element implicitly has an 'any' type" in TypeScript Object Indexing
Learn why TypeScript rejects string object indexing and how to fix it with keyof, unions, and typed object keys in React.
Fix "Property has no initializer" in Angular TypeScript Components
Learn why Angular TypeScript shows "Property has no initializer" and how to fix it using defaults, optional properties, or definite assignment.