如何在TypeScript中的`window`上显式设置新属性?

How do you explicitly set a new property on `window` in TypeScript?

我通过在window上显式设置属性来为我的对象设置全局命名空间。

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

typescript在MyNamespace下加下划线,并抱怨:

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

我可以通过将MyNamespace声明为环境变量并删除window的明确性来使代码工作,但我不想这样做。

1
2
3
declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

我怎样才能让window保持在里面,使typescript快乐呢?

作为旁注,我发现typescript的抱怨特别有趣,因为它告诉我windowany类型,它肯定可以包含任何内容。


刚刚在另一个stackoverflow问题的答案中找到了答案。

1
2
3
4
5
declare global {
    interface Window { MyNamespace: any; }
}

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

基本上,您需要扩展现有的window接口来告诉它您的新属性。


要保持动态,只需使用:

1
(window).MyNamespace


从typescript ^3.4.3开始,此解决方案不再有效

或者…

您只需键入:

1
window['MyNamespace']

您不会得到编译错误,它的工作原理与键入window.MyNamespace相同。


使用TSX?其他答案对我来说都不管用。

我是这样做的:

1
(window as any).MyNamespace


接受的答案是我以前用过的,但是用了0.9.*字体,它就不工作了。window接口的新定义似乎完全取代了内置定义,而不是对其进行扩充。

我已经开始这样做了:

1
2
3
4
5
interface MyWindow extends Window {
    myFunction(): void;
}

declare var window: MyWindow;

更新:使用typescript 0.9.5,接受的答案将再次工作。


如果需要使用需要使用import的自定义类型扩展window对象,可以使用以下方法:

窗口.d.ts

1
2
3
4
5
6
7
import MyInterface from './MyInterface';

declare global {
    interface Window {
        propName: MyInterface
    }
}

请参见手册"声明合并"部分中的"全局扩充":https://www.typescriptlang.org/docs/handbook/declaration merging.html全局扩充


全球都是"邪恶的":)我认为拥有可移植性的最好方法是:

首先导出接口:(例如:/custom.window.ts)

1
2
3
export interface CustomWindow extends Window {
    customAttribute: any;
}

第二次导入

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

第三个具有自定义窗口的强制转换全局变量窗口

1
declare let window: CustomWindow;

通过这种方式,如果您使用window对象的现有属性,那么在不同的ide中也没有红线,因此最后尝试:

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

用typescript 2.4.x测试


我不需要经常这样做,我只有在使用ReduxDevTools和中间件时才会这样做。

我只是做了:

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

或者你可以这样做:

江户十一〔11〕。

然后是myWindow.myProp = 'my value';


如果您使用的是Angular CLI,那么非常简单:

SRC/Polyfills.ts型

1
2
3
4
5
declare global {
  interface Window {
    myCustomFn: () => void;
  }
}

我的自定义实用程序

1
2
3
window.myCustomFn = function () {
  ...
};

如果您使用的是Intellij,则在新的polyfills拾取之前,还需要在IDE中更改以下设置:

1
2
3
4
5
> File
> Settings
> Languages & Frameworks
> TypeScript
> check 'Use TypeScript Service'.


在找到答案后,我认为这个页面可能会有所帮助。https://www.typescriptlang.org/docs/handbook/declaration merging.html全局扩充不确定声明合并的历史,但它解释了以下内容可以工作的原因。

1
2
3
4
5
declare global {
    interface Window { MyNamespace: any; }
}

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

其他大多数答案都不完美。

  • 其中一些只是抑制了Shop的类型推断。
  • 其他一些只关心全局变量作为名称空间,而不是作为接口/类。

今天早上我也遇到了类似的问题。我尝试了这么多这样的"解决方案",但它们中没有一个绝对不会产生类型错误,并且能够在IDE中触发类型跳转(webstorm或vscode)。

最后,从这里开始

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

我找到了一个合理的解决方案来附加作为接口/类和命名空间的全局变量的类型。

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
// typings.d.ts
declare interface Window {
    myNamespace?: MyNamespace & typeof MyNamespace
}

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

现在,上面的代码将名称空间MyNamespace和接口MyNamespace的类型合并到全局变量MyNamespace中(window的属性)。


如果您使用的是typescript定义管理器,下面介绍如何执行此操作!

1
npm install typings --global

创建typings/custom/window.d.ts

1
2
3
4
5
interface Window {
  MyNamespace: any;
}

declare var window: Window;

安装自定义键入:

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

完成,使用它‌!typescript不会再抱怨了:

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


供参考(这是正确答案):

.d.ts定义文件中

1
type MyGlobalFunctionType = (name: string) => void

如果在浏览器中工作,通过重新打开窗口的界面,可以将成员添加到浏览器的窗口上下文中:

1
2
3
interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Nodejs的想法相同:

1
2
3
4
5
declare module NodeJS {
  interface Global {
    myGlobalFunction: MyGlobalFunctionType
  }
}

现在,您声明根变量(它将实际位于窗口或全局上)

1
declare const myGlobalFunction: MyGlobalFunctionType;

然后在一个常规的.ts文件中,但作为副作用导入,您实际上实现了它:

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

最后在代码库的其他地方使用它,可以使用:

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

myGlobalFunction("Kevin");


使自定义接口扩展窗口并将自定义属性添加为可选。

然后,让使用自定义接口但与原始窗口一起值的custom window。

它与[email protected]一起工作。

1
2
3
4
5
6
7
interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {}

如果您使用的是typescript 3.x,则可以在其他答案中省略declare global部分,而只使用:

1
2
3
4
interface Window {
  someValue: string
  another: boolean
}

在使用typescript 3.3、webpack和tslint时,这对我很有用。


我今天想在一个角度(6)的图书馆使用这个,我花了一段时间才使它按预期工作。

为了让我的库使用声明,我必须对声明全局对象新属性的文件使用d.ts扩展名。

最后,文件的结尾是:

江户十一〔一〕号

一旦创建,不要忘记在你的public_api.ts中暴露它。

那是为了我。希望这有帮助。


对于那些想要在window对象上设置计算属性或动态属性的人,您会发现使用declare global方法是不可能的。为这个用例澄清

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

你可能会尝试这样做

1
2
3
4
5
declare global {
  interface Window {
    [DyanmicObject.key]: string; // error RIP
  }
}

但是,上面的内容会出错。这是因为在typescript中,接口不能很好地处理计算属性,并且会引发如下错误

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

为了解决这个问题,你可以建议把window铸造成,这样你就可以做到了。

1
(window as any)[DynamicObject.key]


首先,您需要在当前作用域中声明窗口对象。因为typescript想要知道对象的类型。因为窗口对象是在其他地方定义的,所以不能重新定义它。但你可以声明如下:

1
declare var window: any;

这不会重新定义窗口对象,也不会创建另一个名为window的变量。这意味着窗口是在其他地方定义的,而您只是在当前范围内引用它。

然后,您可以通过以下方式引用mynamespace对象:

1
window.MyNamespace

或者您可以通过以下方法在window对象上设置新属性:

1
window.MyNamespace = MyObject

现在打字稿也不会抱怨了。