Question

How to Add a Custom Property to window in TypeScript

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 any everywhere
  • 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 MyNamespace is 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.

Take Quiz

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:

  1. TypeScript reads the Window interface extension.
  2. It learns that window may contain a property named MyNamespace.
  3. The assignment window.MyNamespace = window.MyNamespace || {} is now valid because MyNamespace is a known property.
  4. If window.MyNamespace already exists, it is reused.
  5. If it does not exist yet, {} is assigned.
  6. The next line sets count to 1.

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, or window.Config
    • TypeScript can support this safely with interface augmentation
  • 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.

Take Quiz

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 MyNamespace exists.

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

ApproachExampleType SafetyBest Use
Extend Windowinterface Window { MyNamespace: {} }HighBest general solution
Cast to any(window as any).MyNamespaceLowQuick temporary workaround
Declare global variabledeclare var MyNamespace: anyLow to mediumLegacy code that does not need window.
Use module/global config object insteadconst config = {...}HighPreferred when a true global is not needed

window.MyNamespace vs declare var MyNamespace

OptionWhat it means
window.MyNamespaceA property stored on the browser global object
declare var MyNamespaceA 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 choiceResult
anyFast to write, but removes safety
Record<string, unknown>Flexible and safer
Exact object typeBest autocomplete and error checking
Take Quiz

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.ts for 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.

Take Quiz

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 AppConfig property on the Window type.
  • Initialize window.AppConfig if it does not already exist.
  • Store at least two configuration values.
  • Read and print those values in TypeScript code.
Take Quiz

Keep learning

Related questions

View more