How do you explicitly set a new property on `windo

2019-01-01 01:48发布

问题:

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.

回答1:

Just found the answer to this in another StackOverflow 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.



回答2:

To keep it dynamic, just use:

(<any>window).MyNamespace


回答3:

Or...

you can just type:

window[\'MyNamespace\']

and you wont get a compile error and it works the same as typing window.MyNamespace



回答4:

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

Here\'s what I did:

(window as any).MyNamespace


回答5:

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.



回答6:

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: https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation



回答7:

Global are \"evil\" :), i think the best way to have also the portability is:

First you export the interface: (eg: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Second you import

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

Third cast global var window with CustomWindow

declare let window: CustomWindow;

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

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

Tested with Typescript 2.4.x



回答8:

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\';



回答9:

Most of the other answers are not perfect.

  • Some of them just suppress the type inference for shop.
  • Some of the others only cares about global variable as namespace, but not as interface/class

I also encounter the similar problem this morning. I tried so many \"solutions\" on SO, but none of them produce no type error absolutely and enable triggering type jumping in IDE(webstorm or vscode).

Finally, from here

https://github.com/Microsoft/TypeScript/issues/3180#issuecomment-102523512

, I find a reasonable solution to attach typings for global variable which acts as interface/class and namespace both.

Example is below:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

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



回答10:

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 anymore:

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


回答11:

If you\'re using the Angular CLI it\'s really straightforward (tested on CLI RC.0):

src/polyfills.ts

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

my-custom-utils.ts

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

I\'m using IntelliJ, so I also needed to change the following setting in the IDE before my new polyfills picked up:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check \'Use TypeScript Service\'.


回答12:

After finding answers around, I think this page might be helpful. https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation 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 || {};


回答13:

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
}

Same idea for NodeJS:

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\");


回答14:

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 extention 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. Hope this helps.



回答15:

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

Then, let the customWindow that 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 {} 


标签: typescript