1275

I setup global namespaces for my objects by explicitly setting a property on window.

window.MyNamespace = window.MyNamespace || {};

TypeScript underlines MyNamespace and complains that:

The property 'MyNamespace' does not exist on value of type 'window' any"

I can make the code work by declaring MyNamespace as an ambient variable and dropping the window explicitness but I don't want to do that.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

How can I keep window in there and make TypeScript happy?

As a side note I find it especially funny that TypeScript complains since it tells me that window is of type any which by definitely can contain anything.

3

32 Answers 32

1524

I just found the answer to this in another Stack Overflow question's answer.

declare global {
    interface Window { MyNamespace: any; }
}

window.MyNamespace = window.MyNamespace || {};

Basically, you need to extend the existing window interface to tell it about your new property.

14
  • 73
    Note the capital W in Window. That tripped me up.
    – ajm
    Commented Oct 9, 2013 at 20:55
  • 2
    I couldn't get this to compile with tsc 1.0.1.0. Blake Mitchell's answer did work for me, though.
    – Pat
    Commented Oct 13, 2014 at 17:51
  • 71
    The declare global { interface Window { ... } } works with TypeScript 2.5.2, no need for .d.ts file as mentioned above
    – tanguy_k
    Commented Sep 3, 2017 at 20:29
  • 27
    declare interface Window { __GLOBAL_VAR__: any }
    – towry
    Commented Jun 25, 2019 at 5:56
  • 35
    if you get the error "TS2339: Property 'MyNamespace' does not exist on type 'Window & typeof globalThis'." then add export {} at the beginning of the file.
    – Mahdi Abdi
    Commented Jun 23, 2020 at 7:47
568

To keep it dynamic, just use:

(<any>window).MyNamespace

Note that this may not work with TSX because the compiler might think that the <any> is a TSX element. Check out this answer for type assertion that is compatible with TSX.

18
  • 9
    Any ideas how to do this in a tsx file?
    – velop
    Commented Mar 16, 2017 at 7:06
  • 3
    @martin: The <any> makes it explicit, so it works just fine, I believe. Others: If you are using Typescript with React or other JSX environment (resulting in TSX syntax) you'll have to use as instead of <>. See @david-boyd's answer below.
    – Don
    Commented Aug 22, 2017 at 15:59
  • 57
    I had to use (window as any).MyNamespace Commented Sep 19, 2017 at 9:37
  • 1
    I have had to disable tslint on that line with /* tslint:disable */ Commented Feb 16, 2018 at 6:43
  • 5
    I believe taking this approach doesn't work well with the spirit of TypeScript. When I write my projects in TS, I'm trying to improve my ability to spot errors before the user does. This approach merely silences the type checker, but doesn't ensure correct usage of the member which is being defined. Commented Oct 13, 2022 at 10:32
311

Using Svelte or TSX? None of the other answers were working for me.

Here's what I did:

(window as any).MyNamespace
7
  • 6
    Same as (<any> window).MyNamespace actually Commented May 15, 2017 at 12:17
  • 65
    It is the same except when using TSX, because the <any> gets interpreted as JSX, not a type cast.
    – Jake Boone
    Commented May 15, 2017 at 20:40
  • 1
    Thanks @David, this worked like a charm with new Create React App and typescript version, Previously this was working : (<any>window).MyNamespace, but now it is breaking in new typescript version 3.5.x. Commented Jul 9, 2019 at 15:58
  • 13
    window as any? Then why would you use typescript? Just use Javascript x)
    – MarcoLe
    Commented Mar 19, 2020 at 9:09
  • 7
    Funny how people use Typescript and then silence it. It's as good as not using it at all. And to think that these answers are the most upvoted ones! Sounds promising...
    – jperl
    Commented Oct 6, 2021 at 8:41
234

As of TypeScript ^3.4.3, this solution no longer works

Or...

you can just type:

window['MyNamespace']

And you won’t get a compile error and it works the same as typing window.MyNamespace.

11
  • 24
    but you will probably get a tslint error... If you have one of course
    – smnbbrv
    Commented Jan 7, 2016 at 12:34
  • 86
    This completely flies in the face of strong typing, the whole idea behind TypeScript.
    – d512
    Commented Jan 28, 2016 at 23:55
  • 22
    @user1334007 using globals does as well. However, some legacy code requires it. Commented Mar 1, 2016 at 17:03
  • 8
    Does not work in ^3.4.3. TypeScript error: Element implicitly has an 'any' type because type 'Window' has no index signature. TS7017
    – Green
    Commented Apr 16, 2019 at 12:04
  • 10
    I really must echo @Auspex. TypeScript isn't a thing you should be trying to get around. If you're using TS, embrace it and get the types right. If you don't want to do that, don't use TypeScript. If you're being forced to use it but don't want to, make the case to your manager to switch away from TS, or get help getting the typings right. It's exceedingly unprofessional and petulant to muddy and disrupt a TS codebase as a passive aggressive swipe at strong/static typing.
    – Patrick
    Commented Sep 12, 2019 at 15:53
142

Globals are "evil" :) I think the best way to also have the portability is:

First you export the interface: (for example, ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Second, you import

import {CustomWindow} from './custom.window.ts';

Third, cast the global variable window with CustomWindow:

declare let window: CustomWindow;

In this way you also don't have a red line in a different IDE if you use it with existent attributes of the window object, so at the end try:

window.customAttribute = 'works';
window.location.href = '/works';

Tested with TypeScript 2.4.x and newest!

11
  • 5
    you might want to expand on why globals are evil. In our case we're using a non standardized API on the window object. It is polyfilled for now. Is that truely evil? Commented Nov 7, 2018 at 8:01
  • 4
    @MathijsSegers i don't know especially your case but.. about globals are evil is not only about javascript.. is in every language.. some reason are: - You can't apply well design pattern - Memory and performance issue (You have them everywhere, try to attach something to String Object and see what happens when you do new String) - Name collision causing side effect on code.. (especially on javascript that have async nature) I can continue but i think you can image the rest...
    – onalbi
    Commented Nov 7, 2018 at 21:14
  • 4
    @onabi I'm literally talking about a feature of the browser. It's globally available since it's the browser. You really would suggest it's still wrong to go for global definitions? I mean we could implement Ponyfills but this would bloat browsers which actually support the feature (e.g. fullscreen api). That said I don't believe that all globals are evil perse. Commented Nov 8, 2018 at 6:54
  • 3
    @MathijsSegers in my opinion the global variable should be avoid to be used or modified where we can.. is true that window is available since browser exist but is also true that was in mutation all the time.. so if for example we define now window.feature = 'Feature'; and this is used in massive way on code.. what happen if window.feature is added by browsers on all code this feature is override.. anyway i gave a explanation of my sentence is not to go against you.. regards...
    – onalbi
    Commented Nov 8, 2018 at 9:10
  • 2
    Globals are not "evil"; there is a place for such things, otherwise they would be removed from programming languages. Sometimes a global variable really is the best way to implement something. What is evil, is "overuse of globals". Commented Feb 12, 2021 at 20:55
107

It's straightforward:

File src/polyfills.ts

declare global {
  interface Window {
    myCustomFn: () => void;
  }
}

File my-custom-utils.ts

window.myCustomFn = function () {
  ...
};

If you're using IntelliJ IDEA, you also needed to change the following setting in the IDE before your new polyfills pick up:

> File
> Settings
> Languages & Frameworks
> TypeScript
> check 'Use TypeScript Service'.
3
  • 8
    declare global is the trick, and this answer is not really specific to Angular CLI... Commented Apr 6, 2020 at 1:07
  • yeah. its the correct one. Commented Sep 6, 2023 at 14:14
  • Note that, depending on your setup and your declaration files, you might need declare interface Window without the declare global wrapper. Commented Oct 4, 2023 at 20:15
84

The accepted answer is what I used to use, but with TypeScript 0.9.* it no longer works. The new definition of the Window interface seems to completely replace the built-in definition, instead of augmenting it.

I have taken to doing this instead:

interface MyWindow extends Window {
    myFunction(): void;
}

declare var window: MyWindow;

UPDATE: With TypeScript 0.9.5 the accepted answer is working again.

4
  • 3
    This works also with modules as used by TypeScript 2.0.8. Example: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() Then you can call foo() e.g. from the Chrome Dev Tools console like mc.foo() Commented Dec 5, 2016 at 16:43
  • This is a very nice answer if you don't want to declare something as global. On the other side, you need to call declare var... in every file you need.
    – Puce
    Commented May 25, 2018 at 12:23
  • Plus one for this approach because in this case you don't conflict with the other packages which extend the global window in the monorepo. Commented Oct 4, 2018 at 6:55
  • Thank you! Best answer IMO. One should not override Window for the simple fact that it is not a vanilla window. Just circumventing type checking or trying to fool TS also isn't the way to do it. Commented Sep 18, 2019 at 11:13
71

If you need to extend the window object with a custom type that requires the use of import, you can use the following method:

window.d.ts

import MyInterface from './MyInterface';

declare global {
    interface Window {
        propName: MyInterface
    }
}

See Global Augmentation in the 'Declaration Merging' section of the Handbook.

0
58

Create a file called global.d.ts, e.g., /src/@types/global.d.ts, and then define an interface like:

interface Window {
  myLib: any
}

Reference: Global .d.ts

0
49

Most of the other answers are not perfect.

  • Some of them just suppress the type inference for show.
  • Some of the others only care about global variables as namespaces, but not as interfaces/classes

I also encountered a similar problem this morning. I tried so many "solutions" on Stack Overflow, but none of them produced absolutely no type errors and enabled triggering type jumping in the IDE (WebStorm or Visual Studio Code).

Finally, from Allow module definitions to be declared as global variables #3180

I found a reasonable solution to attach typings for a global variable that acts as interface/class and namespace both.

The example is below:

// typings.d.ts
declare interface Window {
    myNamespace?: MyNamespace & typeof MyNamespace
}

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

The code above merges the typings of namespace MyNamespace and interface MyNamespace into the global variable myNamespace (the property of window).

0
37

I don't need to do this very often. The only case I have had was when using Redux DevTools with middleware.

I simply did:

const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

Or you could do:

let myWindow = window as any;

And then myWindow.myProp = 'my value';

3
  • 1
    In this case you can also install the npm package, which contains all the definitions - as specified in the docs
    – bbrinx
    Commented Jan 30, 2019 at 21:48
  • You can do this, but that's not the answer to the question, but rather a workaround
    – Vitalij
    Commented Feb 13, 2019 at 9:04
  • Thanks to this, I could enable redux tools properly under typescript by using (window as any).__REDUX_DEVTOOLS_EXTENSION__ && (window as any).__REDUX_DEVTOOLS_EXTENSION__()) Commented May 5, 2019 at 11:25
22

After finding answers around, I think this page might be helpful:

Global augmentation

I am not sure about the history of declaration merging, but it explains why the following could work.

declare global {
    interface Window { MyNamespace: any; }
}

window.MyNamespace = window.MyNamespace || {};
0
22

From the version 3.4, TypeScript has supported globalThis. See Type-checking for globalThis.

From the above link:

// in a global file:
var abc = 100;
// Refers to 'abc' from above.
globalThis.abc = 200;
window.abc = 300; // window object can also be used.

Playground

A "global" file is a file which does not have any import/export statements. So the declaration var abc; can be written in .d.ts.

2
  • 1
    Yes but does it declare the variable on the window object, which is the question asked? I highly doubt that
    – Tofandel
    Commented Jul 12, 2021 at 16:57
  • 5
    It does. lib.d.ts declares window as type Windows & globalThis, meaning the two types get merged together. Commented Jul 29, 2021 at 16:44
19

If you are using TypeScript 3.x, you may be able to omit the declare global part in the other answers and instead just use:

interface Window {
  someValue: string
  another: boolean
}

This worked with me when using TypeScript 3.3, Webpack and TSLint.

2
18

Using create-react-app v3.3 I found the easiest way to achieve this was to extend the Window type in the auto-generated react-app-env.d.ts:

interface Window {
    MyNamespace: any;
}
17

TypeScript does not perform typechecking on string properties.

window["newProperty"] = customObj;

Ideally, the global variable scenario should be avoided. I use it sometimes to debug an object in the browser console.

17
// In typings.d.ts(is Global)
export declare global {
    interface Window {
        __PUBLIC__: string;
    }
}

enter image description here

1
  • This worked nicely for me and feels an explicit way of defining the type globally where that's applicable. I'll add that in my case I wanted to add Cypress and app which had imported types. I was able to nicely achieve this by importing both types from their respective libs and declaring the attributes accordingly: ``` import { Cypress } from 'cypress'; import { ComponentPublicInstance } from 'vue'; // Add Cypress and app to global window for testing export declare global { interface Window { Cypress: Cypress; app: ComponentPublicInstance; } } ```
    – aleph_one
    Commented May 23, 2022 at 20:32
16

Here's how to do it, if you're using TypeScript Definition Manager!

npm install typings --global

Create typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Install your custom typing:

typings install file:typings/custom/window.d.ts --save --global

Done! Use it! TypeScript won't complain any more:

window.MyNamespace = window.MyNamespace || {};
1
  • 3
    FWIW typings was made obsolete with Typescript 2.0 (mid 2016), and has been archived by the owner.
    – mrm
    Commented Dec 11, 2018 at 2:52
16

Using

window["MyNamespace"] = window["MyNamespace"] || {};

should be all right as it is using a string property, but if you really want to have a separated window and organised your code, you can extend the window object:

interface MyNamespacedWindow extends Window {
    MyNamespace: object;
}

declare var window: MyNamespacedWindow;
1
  • I like your second solution, but it throws a TS error on declare saying Modifiers cannot appear here. ts(1184). Any ideas?
    – TinyTiger
    Commented Feb 5, 2022 at 23:38
12

First you need to declare the window object in the current scope. Because TypeScript would like to know the type of the object. Since the window object is defined somewhere else, you can not redefine it.

But you can declare it as follows:

declare var window: any;

This will not redefine the window object nor will it create another variable with name window. This means window is defined somewhere else and you are just referencing it in the current scope.

Then you can refer to your MyNamespace object simply by:

window.MyNamespace

Or you can set the new property on the window object simply by:

window.MyNamespace = MyObject

And now the TypeScript won’t complain.

0
12

[2022]:We have to extend the "window" object in our React or Nextjs project . We can use the following step to solve this issue.

  • Make a folder inside src folder name as types.

  • Make a file inside types folder name as index.d.ts

  • write this code inside index.d.ts file. enter image description here

    export {};
     declare global {
       interface Window {
        NameSpace: any;
       }
    
     }
     window.NameSpace= window.NameSpace|| {};
    

enter image description here save this file.

Now one last change .

Change the "tsConfig.json" file. to inherit both the node module type and our types.

{
  "compilerOptions": {
   ...
    "typeRoots": [
      "./node_modules/@types",
      "./src/types"
    ],
 ....
}

enter image description here

1
  • of all the things I tried, this worked for me.
    – mkamran94
    Commented Feb 8 at 20:30
11

Full & Short answer

1- Add typeRoots property to tsconfig.json:

{
  "compilerOptions": {
    ...
    "typeRoots": ["src/@types", "node_modules/@types"]
    ...
  }
}

2- Extend Window type

// file: src/@types/global.d.ts

declare global {
    interface Window { customProperty: <type>; }
}
9

For reference (this is the correct answer):

Inside a .d.ts definition file

type MyGlobalFunctionType = (name: string) => void

If you work in the browser, you add members to the browser's window context by reopening Window's interface:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

The same idea for Node.js:

declare module NodeJS {
  interface Global {
    myGlobalFunction: MyGlobalFunctionType
  }
}

Now you declare the root variable (that will actually live on window or global):

declare const myGlobalFunction: MyGlobalFunctionType;

Then in a regular .ts file, but imported as side-effect, you actually implement it:

global/* or window */.myGlobalFunction = function (name: string) {
  console.log("Hey !", name);
};

And finally use it elsewhere in the codebase, with either:

global/* or window */.myGlobalFunction("Kevin");

myGlobalFunction("Kevin");
0
8

For those who want to set a computed or dynamic property on the window object, you'll find that not possible with the declare global method. To clarify for this use case

window[DynamicObject.key] // Element implicitly has an 'any' type because type Window has no index signature

You might attempt to do something like this

declare global {
  interface Window {
    [DyanmicObject.key]: string; // error RIP
  }
}

The above will error though. This is because in TypeScript, interfaces do not play well with computed properties and will throw an error like

A computed property name in an interface must directly refer to a built-in symbol

To get around this, you can go with the suggest of casting window to <any> so you can do

(window as any)[DynamicObject.key]
0
7

Make a custom interface that extends the Window and add your custom property as optional.

Then, let the customWindow use the custom interface, but valued with the original window.

It's worked with the TypeScript 3.1.3.

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {}
5

Two method:

#1: create a global.d.ts file, this file name can be random but it needs to be included in TypeScript checked directory, then you can use declare global namespace to define new properties on Window:

declare global {
  interface Window {
    myCustomFn: () => void;
  }
}

// At least one export statement
export {};

#2: create a global.d.ts file, this file name can be random but it needs to be included in TypeScript checked directory, and you can't use any import or export statement in this file. then you can just use interface to define new properties on Window:


//~ just need this
interface Window {
  myProperty: string;
}
4
(window as { test: string } & Window & typeof globalThis).test = `Hello World`;
2

I wanted to use this in an Angular (6) library today and it took me a while to get this to work as expected.

In order for my library to use declarations, I had to use the d.ts extension for the file that declares the new properties of the global object.

So in the end, the file ended up with something like:

/path-to-angular-workspace/angular-workspace/projects/angular-library/src/globals.d.ts

Once created, don't forget to expose it in your public_api.ts.

That did it for me.

0
// Typescript 5.2.2
// You just code it in xxx.d.ts for extend window:
declare interface Window {
  foo: string;
}
0

One liner with simple Typescript:

(window as typeof window & { myProperty: any }).myProperty
1
  • Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation? Commented Jun 26 at 1:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.