1203

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.

joshuapoehls
  • 32,695
  • 11
  • 50
  • 61
  • Some of the answers are the subject of [a meta question](https://meta.stackoverflow.com/questions/412736/how-can-i-tackle-this-edit). – Peter Mortensen Nov 01 '21 at 12:59
  • The answer [stackoverflow.com/a/47130953](https://stackoverflow.com/a/47130953) also has a link to the TypeScript documentation: [Augmenting global/module scope from modules](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-8.html#augmenting-globalmodule-scope-from-modules). – kca Aug 01 '23 at 18:09

30 Answers30

1420

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
joshuapoehls
  • 32,695
  • 11
  • 50
  • 61
  • 61
    Note the capital W in Window. That tripped me up. – ajm Oct 09 '13 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 Oct 13 '14 at 17:51
  • I needed to test for PointerEvent as a feature check, which TypeScript didn't like so adding the interface line solved my problem without declaring the variable and thus not affecting the test (TypeScript 1.5.3.0) Thanks – Mog0 Jul 23 '15 at 11:14
  • In the newest Angular versions, just use the first line. This worked for me. – O.Olivier Feb 14 '17 at 14:51
  • 1
    We should probably use an optional property like this: `interface Window { MyNamespace?: any; }` – xcatliu Jun 28 '17 at 05:30
  • 71
    The `declare global { interface Window { ... } }` works with TypeScript 2.5.2, no need for `.d.ts` file as mentioned above – tanguy_k Sep 03 '17 at 20:29
  • 2
    At first typescript told me: `error TS1234: An ambient module declaration is only allowed at the top level in a file.`, when I put it at the top of the file, it did fix that error, so you might have to do that as well. – Zelphir Kaltstahl Nov 16 '17 at 10:20
  • 1
    I'm getting "TS2339: Property 'MyNamespace' does not exist on type 'Window'. I'm using window in a different file. – Totty.js Nov 02 '18 at 03:02
  • 2
    Trick: you must add some import (like `import * as React from 'react';`) if you put these in index.d.ts – zhangciwu Mar 01 '19 at 07:57
  • 24
    `declare interface Window { __GLOBAL_VAR__: any }` – towry Jun 25 '19 at 05:56
  • 30
    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 Jun 23 '20 at 07:47
  • This answer says "extend the existing `window` interface` - not to be pedantic, but this is declarataion merging, not extension: https://www.typescriptlang.org/docs/handbook/declaration-merging.html. In case anyone wants to know about the limitations and trade-offs. – Max Heiber Dec 29 '20 at 18:14
  • If you need to import an external type, check this answer below: https://stackoverflow.com/a/40204572 – Maciej Krawczyk May 15 '21 at 07:06
  • Just do this: https://medium.com/@ohansemmanuel/how-do-you-explicitly-set-a-new-property-on-window-in-typescript-e144cfecf8e7 – Md. A. Apu Dec 10 '22 at 13:44
559

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.

MT0
  • 143,790
  • 11
  • 59
  • 117
chinupson
  • 6,117
  • 1
  • 16
  • 8
  • 9
    Any ideas how to do this in a tsx file? – velop Mar 16 '17 at 07:06
  • 3
    @martin: The 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](https://stackoverflow.com/questions/12709074/how-do-you-explicitly-set-a-new-property-on-window-in-typescript#38964459). – Don Aug 22 '17 at 15:59
  • 56
    I had to use (window as any).MyNamespace – Arsalan Ahmad Sep 19 '17 at 09:37
  • Similarly for `this` - `return ( this)['something in scope']` – HankCa Jan 23 '18 at 05:58
  • 1
    I have had to disable tslint on that line with `/* tslint:disable */` – Michael Yagudaev Feb 16 '18 at 06:43
  • This is by far my favorite solution, as someone someone who's used to working in vanilla JS and being able to access the window object without any problems. Similar to https://stackoverflow.com/a/46025351/1295378 which elaborates further – Drkawashima Dec 12 '19 at 19:55
  • I also used this style, but now I switch to eslint (https://code.visualstudio.com/api/advanced-topics/tslint-eslint-migration) and this style leads to some warnings: First you get `Use 'as any' instead of ''.eslint@typescript-eslint/consistent-type-assertions` and when switch to `as any` you get `Unexpected any. Specify a different type.eslint@typescript-eslint/no-explicit-any` I know I could disable those warnings but I would like keep it clean without disabling if possible. – John Archer Apr 15 '20 at 16:11
  • can also do this once into a variable like `var anyWindow = window;` and then use the `anyWindow` like you would `window` after that. – Pierre Nov 06 '20 at 09:18
  • This was a bad edit @Retsam, the version you edited to is already an answer below, and you fundamentally changed the answer. Please keep this as `(window).MyNamespace` – Daniel Oct 29 '21 at 03:31
  • @Daniel The ` value` syntax is just an old, legacy syntax for the new, preferred, (and works in JSX files)`value as Type` syntax. They're completely identical in behavior and it's confusing for this answer to show the old legacy syntax which has mostly been scrubbed from TS's documentation. Yes, this makes it fundamentally the same as an answer given later: but it seems bad for the top voted answer to use the worse syntax. Especially since the two most upvoted comments are people having issue with this answer. – Retsam Oct 29 '21 at 16:57
  • @Retsam But the question is not about TSX, it's about TS. And it's not the top voted answer, it's the second top-most voted answer. It's also valid TS. I also don't think it would be good practise to change every highly rated answer to the "best" answer just because it's highly rated. This defeats the purpose of voting and posting answers. – Daniel Oct 30 '21 at 04:40
  • Let me try changing the answer to give helpful context, without changing the actual answer, let me know what you think @Retsam – Daniel Oct 30 '21 at 04:43
  • @Daniel TSX was the original motivation for introducing a new syntax, but the `as` syntax is now the recommended syntax, regardless of whether you're writing TSX or not. There's just no reason for the language to have two identical syntaxes for doing the same thing, and it's confusing for people who aren't familiar with this legacy syntax to see answers using it. Saying "use this if you're using JSX" is just not necessary. – Retsam Oct 31 '21 at 17:08
  • Yes, it makes this answer identical to a second answer, but honestly, that person shouldn't have put up an answer that's identical except for using a different syntax - they should have edited this answer to update to the modern syntax. – Retsam Oct 31 '21 at 17:09
  • I've put up my own edit as a compromise - this lists both answers, so it's not identical to the other answer, but primarily lists the newer, preferred syntax and drops the mention of TSX. – Retsam Oct 31 '21 at 17:11
  • This makes absolute no sense @Retsam, your logic for changing this one is because it was "the highest answer" on this question but that's not true at all. Also the answer already exists. Reverting it. – Daniel Nov 01 '21 at 05:06
  • I put this question to a meta post @Retsam https://meta.stackoverflow.com/q/412736/1250319 and one excellent point is that Stackoverflow is about *accumulating* knowledge, not *destroying* it. The syntax is old yes but not deprecated. Do not edit again please. – Daniel Nov 01 '21 at 15:02
  • 2
    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. – Tess E. Duursma Oct 13 '22 at 10:32
307

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

Here's what I did:

(window as any).MyNamespace
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
David Boyd
  • 6,501
  • 3
  • 21
  • 13
  • 5
    Same as [`( window).MyNamespace`](http://stackoverflow.com/a/30740935/4554883) actually – Parzh from Ukraine May 15 '17 at 12:17
  • 65
    It is the same except when using TSX, because the `` gets interpreted as JSX, not a type cast. – Jake Boone May 15 '17 at 20:40
  • 1
    Thanks @David, this worked like a charm with new Create React App and typescript version, Previously this was working : (window).MyNamespace, but now it is breaking in new typescript version 3.5.x. – Jignesh Raval Jul 09 '19 at 15:58
  • 11
    window as any? Then why would you use typescript? Just use Javascript x) – MarcoLe Mar 19 '20 at 09:09
  • @MarcoLe For instance, Angular uses TypeScript and if you want to develop a logger that can switch properties on it at runtime, you can do so in the browser's dev console with this technique. – marckassay Oct 29 '20 at 19:07
  • 6
    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 Oct 06 '21 at 08:41
  • It's worth noting the big downside of this solution. You're turning off type checking for this part of your codebase. Make sure you're comfortable and ensure disabling TypeScript's ability to warn you of problems. – Kristian Roebuck Nov 02 '22 at 10:04
231

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Evan Larsen
  • 9,935
  • 4
  • 46
  • 60
  • 24
    but you will probably get a tslint error... If you have one of course – smnbbrv Jan 07 '16 at 12:34
  • 85
    This completely flies in the face of strong typing, the whole idea behind TypeScript. – d512 Jan 28 '16 at 23:55
  • 21
    @user1334007 using globals does as well. However, some legacy code requires it. – nathancahill Mar 01 '16 at 17:03
  • @user1334007 this is the example when you need this stuff: http://andrewhfarmer.com/aws-sdk-with-webpack/ – SMSk Dec 05 '16 at 15:18
  • how about when we have to call a method? – Ramesh Apr 12 '17 at 12:35
  • 3
    @Ramesh `window['MyNamespace']()` (just add brackets) – illnr Dec 21 '17 at 18:14
  • 1
    @nathancahill Global variables have nothing to do with strong typing! You can have strong, static typing even if your project uses global variables. – bcherny May 08 '18 at 04:21
  • I couldn't do `window:Window` `window.Promise`, but I can `window['Promise']` – Simão Garcia Aug 06 '18 at 14:35
  • 7
    Does not work in `^3.4.3`. `TypeScript error: Element implicitly has an 'any' type because type 'Window' has no index signature. TS7017` – Green Apr 16 '19 at 12:04
  • 3
    @Cody No, not good. If you don't want strong typing, just don't use TypeScript! Bastardizing the language to get around it's basic features is just foolish. – Auspex Aug 27 '19 at 11:09
  • 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 Sep 12 '19 at 15:53
134

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!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
onalbi
  • 2,609
  • 1
  • 24
  • 36
  • 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? – Mathijs Segers Nov 07 '18 at 08: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 Nov 07 '18 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. – Mathijs Segers Nov 08 '18 at 06: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 Nov 08 '18 at 09: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". – deltamind106 Feb 12 '21 at 20:55
  • 1
    Depending on the use-case of course, I prefer this approach too since it allows me to type non-standard API without making it available across the code base. Additionally if you are working on a library you might not want to alter the global scope as your users might start depending on the existence of these types. – Grabofus Mar 18 '21 at 14:11
  • 1
    Globals are not evil, there is a lot of valid use cases for them, like making accessible a library with a public api: eg `window.MyAwesomeLib = someObject;` or a polyfill, it's only evil if you declare every single one of your private vars on it, because you pollute the window object and may run into naming conflicts. But almost nobody does that anyways, and definitely nobody using typescript – Tofandel Jul 12 '21 at 16:53
  • When using this on a Vue component I get a TS error on `declare` saying `Modifiers cannot appear here. ts(1184)`. Any idea what might be the problem? – TinyTiger Feb 05 '22 at 23:51
  • 1
    If globals are not evil, they might be dangerous, as stated above by @onalbi – jckmlczk Apr 06 '22 at 12:27
  • In 2023, you should be importing modules instead of adding them to the window object. – Ruan Mendes Feb 08 '23 at 19:56
96

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'.
mousetail
  • 7,009
  • 4
  • 25
  • 45
Stephen Paul
  • 37,253
  • 15
  • 92
  • 74
83

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.

Blake Mitchell
  • 2,647
  • 1
  • 24
  • 22
  • 2
    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()` – Martin Majewski Dec 05 '16 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 May 25 '18 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. – Dmitrii Sorin Oct 04 '18 at 06: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. – Rutger Willems Sep 18 '19 at 11:13
69

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jeff Camera
  • 5,324
  • 5
  • 44
  • 59
54

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alex Tran
  • 541
  • 4
  • 3
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).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
e-cloud
  • 4,331
  • 1
  • 23
  • 37
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';

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dimitar Nikovski
  • 973
  • 13
  • 15
  • 1
    In this case you can also install the npm package, which contains all the definitions - [as specified in the docs](https://github.com/zalmoxisus/redux-devtools-extension#11-basic-store) – bbrinx Jan 30 '19 at 21:48
  • You can do this, but that's not the answer to the question, but rather a workaround – Vitalij Feb 13 '19 at 09: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__()) – danivicario May 05 '19 at 11:25
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kena0ki
  • 479
  • 6
  • 9
  • 1
    Yes but does it declare the variable on the window object, which is the question asked? I highly doubt that – Tofandel Jul 12 '21 at 16:57
  • 5
    It does. `lib.d.ts` declares `window` as type `Windows & globalThis`, meaning the two types get merged together. – David Wheatley Jul 29 '21 at 16:44
21

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 || {};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sheng
  • 794
  • 8
  • 12
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;
}
Kris Dover
  • 544
  • 5
  • 9
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Irshad
  • 1,016
  • 11
  • 30
17
// In typings.d.ts(is Global)
export declare global {
    interface Window {
        __PUBLIC__: string;
    }
}

enter image description here

Black Plum
  • 389
  • 2
  • 10
  • 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 May 23 '22 at 20:32
16

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dana Woodman
  • 4,148
  • 1
  • 38
  • 35
  • Does not work in `^3.4.3`. This answer worked for me https://stackoverflow.com/a/42841166/1114926 – Green Apr 16 '19 at 12:10
  • *"TSLint has been deprecated as of 2019. Please see this issue for more details: Roadmap: TSLint → ESLint."*. [Source](https://palantir.github.io/tslint/). – Peter Mortensen Nov 06 '21 at 22:05
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;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alireza
  • 100,211
  • 27
  • 269
  • 172
  • I like your second solution, but it throws a TS error on `declare` saying `Modifiers cannot appear here. ts(1184)`. Any ideas? – TinyTiger Feb 05 '22 at 23:38
15

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 || {};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nik Sumeiko
  • 8,263
  • 8
  • 50
  • 53
  • 3
    FWIW `typings` was made obsolete with Typescript 2.0 (mid 2016), and has been archived by the owner. – mrm Dec 11 '18 at 02:52
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mav55
  • 4,080
  • 2
  • 21
  • 20
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>; }
}
Masih Jahangiri
  • 9,489
  • 3
  • 45
  • 51
10

[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

Narendra Maurya
  • 391
  • 2
  • 7
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");
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Benoit B.
  • 3,561
  • 24
  • 19
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]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
aug
  • 11,138
  • 9
  • 72
  • 93
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 {}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
shuizhongyuemin
  • 559
  • 5
  • 11
5
(window as { test: string } & Window & typeof globalThis).test = `Hello World`;
Up209d
  • 4,364
  • 4
  • 13
  • 10
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dirk
  • 21
  • 3
-2

I use without declarate. My example looks like:

enter image description here

bob
  • 157
  • 1
  • 2
-2

I know that question may be too old for new answers, but I recently did that with a method that wasn't mentioned here - Object.defineProperty(). Like this:

Object.defineProperty(window, 'MyNamespace', {
  enumerable: true,
  configurable: true,
  value: MyNamespace || {}
});
Alex Naidovich
  • 188
  • 4
  • 15
-4

TypeScript prevents accessing an object without assigning a type that has the desired property or already assigned to any, so you can use optional chaining:

window?.MyNamespace =  'value'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mohammed Mamoun
  • 425
  • 5
  • 5
  • 4
    this doesn't remove the typescript warning. – loekTheDreamer Jul 02 '21 at 10:19
  • The left-hand side of an assignment expression may not be an optional property access. ts(2779) – qrsngky Jul 18 '23 at 07:42
  • (window as any)?.MyNamespace = 'value' – Mohammed Mamoun Jul 18 '23 at 08:57
  • Evaluating `window?.MyNamespace = 'value'` in JS will actually result in an error (e.g. in Chrome) `Uncaught SyntaxError: Invalid left-hand side in assignment`, so there is a reason why the warning should be there. Whether an actual JS error will be thrown depends on the TS version/config though. For example, for TS4.7.4 targetting ES2019, the output in JS is `window === null || window === void 0 ? void 0 : window.MyNamespace = 'value';` which doesn't throw an error. For TS5.0.4 targetting ES2020 the output is `window?.MyNamespace = 'value';` so there's a runtime error. – qrsngky Jul 25 '23 at 02:11