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(window.MyNamespace.version);
Why export {} is included
If this code is inside a file that should be treated as a module, export {} ensures the file is a module and allows declare global to work correctly.
Simpler .d.ts style example
In a declaration file such as global.d.ts:
interface Window {
MyNamespace: {
version?: string;
};
}
Then in your normal code:
window.MyNamespace = window.MyNamespace || {};
window.MyNamespace.version = "1.0.0";
This is often the cleanest approach in real projects.
If you really want a loose type
interface Window {
MyNamespace: any;
}
This works, but it removes most type safety. Prefer a specific type when possible.
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 undefined:
window.MyNamespace = undefined || {};
That becomes:
window.MyNamespace = {};
Then:
window.MyNamespace.count = 1;
Now the object looks like:
{ count: 1 }
Important detail
TypeScript interface augmentation affects only type checking. It does not create the property at runtime. You still need to assign it in JavaScript or TypeScript code.
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(`${window.AppConfig.apiBaseUrl}/users`);
This pattern is useful when configuration is generated by the server and read by frontend code.
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: "development" | "production";
};
}
Typed globals are often used for config because the values are read in many places.
4. Interop with non-TypeScript scripts
If a script loaded outside the TypeScript build adds something to window, TypeScript code can still use it once Window is extended.
5. Avoiding repeated casts
A poor but common pattern is:
(window as any).MyNamespace = {};
This works, but in a real codebase, repeated any casts make maintenance harder. Proper interface augmentation is cleaner.
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:
interface Window {
MyNamespace: { value?: number };
}
3. Forgetting that type declarations do not create runtime values
Broken assumption:
interface Window {
MyNamespace: { value: number };
}
console.log(window.MyNamespace.value);
Why it can break:
- the interface only tells TypeScript the shape
- it does not initialize
window.MyNamespace
Fix:
window.MyNamespace = window.MyNamespace || { value: 0 };
4. Declaring the wrong type
Broken code:
interface Window {
MyNamespace: string;
}
window.MyNamespace = {};
Why it fails:
- the declared type and assigned value do not match
Fix:
interface Window {
MyNamespace: Record<string, unknown>;
}
5. Putting declare global in the wrong place
If you use:
declare global {
interface Window {
MyNamespace: {};
}
}
inside a module, make sure the file is treated as a module, often by adding:
export {};
Otherwise the declaration may not behave as expected.
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 |
window.MyNamespace vs declare var MyNamespace
| Option | What it means |
|---|---|
window.MyNamespace | A property stored on the browser global object |
declare var MyNamespace | A global variable name available directly |
In browsers, these are often related, but they are not always the best choice semantically in TypeScript. If you specifically want a property on window, extend Window.
any vs specific types
| Type choice | Result |
|---|---|
any | Fast to write, but removes safety |
Record<string, unknown> | Flexible and safer |
| Exact object type | Best autocomplete and error checking |
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.
Key rules
- TypeScript must know the property exists on
Window - Interface declarations affect types only, not runtime values
- Prefer specific types over
any - Use
global.d.tsfor shared global declarations
Good pattern
window.MyNamespace = window.MyNamespace || {};
Common edge case
If strict mode is enabled, make sure your declared type matches what you actually assign.
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 Window.
Where should I put the Window interface declaration?
Usually in a global declaration file such as global.d.ts, or in a module file using declare global.
What type should I use for my custom window property?
Use the most specific type you can. Avoid any unless you truly do not know the shape.
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
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.