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


618

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

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

TypeScript强调MyNamespace并抱怨:

属性“ MyNamespace”在类型为“ window”的值上不存在“

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

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

我怎样才能window留在那里并使TypeScript开心呢?

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


链接
MHS

Answers:


690

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

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

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

基本上,您需要扩展现有window接口以向其介绍新属性。


11
注意窗口中的大写字母W。那使我绊倒了。
2013年

2
我无法使用tsc 1.0.1.0进行编译。不过,布雷克·米切尔(Blake Mitchell)的答案确实对我有用。
2014年

43
请注意,这仅在单独的.d.ts文件中声明时有效。在.ts本身使用导入和导出的文件中使用时,它不起作用。看到这个答案
cdauth

36
declare global { interface Window { ... } }使用TypeScript 2.5.2 的作品,不需要.d.ts如上所述的文件
tanguy_k

2
最初,打字稿告诉我:error TS1234: An ambient module declaration is only allowed at the top level in a file.,当我将其放在文件顶部时,它确实修复了该错误,因此您可能也必须这样做。
Zelphir Kaltstahl,

397

要使其保持动态,只需使用:

(<any>window).MyNamespace

9
任何想法如何在tsx文件中执行此操作?
velop

2
@martin:<any>使它明确,所以我认为它很好用。其他:如果您将Typescript与React或其他JSX环境(使用TSX语法)一起使用as,则必须使用代替<>。参见下面的@ david-boyd的答案

30
我不得不使用(窗口有).MyNamespace
阿尔萨兰·艾哈迈德

类似的this-– return (<any> this)['something in scope']
HankCa,

1
我不得不通过以下方式禁用tslint:/* tslint:disable */
Michael Yagudaev

197

截至TYPESCRIPT ^ 3.4.3,此解决方案不再起作用

要么...

您可以输入:

window['MyNamespace']

而且不会出现编译错误,它的作用与键入相同 window.MyNamespace


15
但是您可能会收到tslint错误...如果您当然有一个
smnbbrv

69
面对强类型打字(TypeScript背后的整个想法),这完全不可行。
d512

18
使用全局变量的@ user1334007也是如此。但是,某些遗留代码需要它。
nathancahill

3
@Ramesh window['MyNamespace']()(只需添加括号)
iBaff

6
“在面对强大的打字技术(TypeScript背后的整个想法)时,这是完全不可行的。” 好!
科迪

188

使用TSX?没有其他答案对我有用。

这是我所做的:

(window as any).MyNamespace


44
除了使用TSX之外,其他方面都是相同的,因为将<any>获取解释为JSX,而不是类型转换。
杰克·布恩

1
感谢@David,这对于新的Create React App和打字稿版本来说就像是一种魅力,以前是这样的:(<any> window).MyNamespace,但是现在它打破了新的打字稿版本3.5.x。
Jignesh Raval

窗口一样吗?那你为什么要用打字稿呢?只需使用Javascript x)
MarcoLe

71

可接受的答案是我曾经使用的答案,但是使用TypeScript 0.9。*时,它将不再起作用。Window接口的新定义似乎完全取代了内置定义,而不是对其进行了扩充。

我已经改为这样做:

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

declare var window: MyWindow;

更新:使用TypeScript 0.9.5,可接受的答案再次起作用。


2
这也适用于TypeScript 2.0.8使用的模块。示例: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() 然后,您可以从Chrome Dev Tools控制台调用foo(),例如 mc.foo()
Martin Majewski

如果您不想将某些内容声明为全局变量,这是一个很好的答案。另一方面,您需要调用所需declare var...的每个文件。
佩斯

为这种方法加上一个,因为在这种情况下,您不会与扩展monorepo中全局窗口的其他软件包冲突。
德米特里·索林

谢谢!最佳答案IMO。一个Window简单的事实,那就是不是一个普通的窗口,不应忽视。仅仅绕过类型检查或试图愚弄TS也不是做到这一点的方法。
罗格·威廉姆斯19'Sep

不幸的是,这从TS 3.6开始不起作用
Jacob Soderlund

65

全局是“邪恶的” :),我认为具有可移植性的最佳方法是:

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

export interface CustomWindow extends Window {
    customAttribute: any;
}

第二您导入

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

第三个使用CustomWindow转换全局变量窗口

declare let window: CustomWindow;

这样,如果使用窗口对象的现有属性,则在不同的IDE中也不会出现红线,因此最后尝试:

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

经过Typescript 2.4.x测试,最新!


1
您可能想扩展一下为什么全球人是邪恶的。在我们的例子中,我们在窗口对象上使用了非标准化的API。现在它已被填充。那真的是邪恶的吗?
Mathijs Segers

1
@MathijsSegers我不特别了解您的情况,但是..关于全局变量的问题不仅与javascript有关..在每种语言中都是..一些原因是:-您不能很好地应用设计模式-内存和性能问题(您让它们无处不在,尝试将某些东西附加到String Object上,看看当您做新的String时会发生什么)-名称冲突在代码上引起副作用。休息...
onalbi

1
@onabi我实际上是在谈论浏览器的功能。它是浏览器,因此在全球范围内均可使用。您真的会建议采用全局定义还是错误的?我的意思是我们可以实现Ponyfills,但这会使实际上支持该功能的浏览器肿(例如,全屏api)。就是说,我不相信所有全球人士都是邪恶的。
Mathijs Segers

2
我认为@MathijsSegers应该避免在可能的地方使用或修改全局变量。.是的,因为存在浏览器,所以窗口是可用的,但也确实是一直在变化的窗口..因此,例如,如果我们现在定义window.feature ='功能'; 并且这在代码上被大量使用..如果浏览器在所有代码上都添加了window.feature,该功能将被覆盖..无论如何,我对我的句子的解释是不违背您的。
onalbi


43

对于使用Angular CLI的用户来说,它很简单:

src / polyfills.ts

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

my-custom-utils.ts

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

如果您使用的是IntelliJ,则还需要在IDE中更改以下设置,然后才能拾取新的polyfills:

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

1
declare global是诀窍,而这个答案并不是真的专门针对Angular CLI ...
Avindra Goolcharan

31

我不需要经常这样做,我遇到的唯一情况是将Redux Devtools与中间件一起使用。

我只是做了:

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

或者您可以这样做:

let myWindow = window as any;

然后 myWindow.myProp = 'my value';


1
在这种情况下,您还可以安装npm软件包,其中包含所有定义- 如文档中所指定
bbrinx

您可以执行此操作,但这不是问题的答案,而是解决方法
Vitalij

由于这一点,我可以通过使用适当能在打字稿Redux的工具(窗口任何).__ REDUX_DEVTOOLS_EXTENSION__ &&(窗口有).__ REDUX_DEVTOOLS_EXTENSION __())
danivicario

20

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

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

今天早上我也遇到类似的问题。我在SO上尝试了很多“解决方案”,但是没有一个绝对不会产生类型错误,并且不会在IDE(webstorm或vscode)中触发类型跳转。

最后,从这里

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

,我找到了一个合理的解决方案,可以为用作接口/类和名称空间的全局变量附加类型

示例如下:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

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


2
谢谢-这似乎是您可以在环境非模块环境中使用的唯一方法。
泰勒·塞巴斯蒂安


13

如果您使用的是TypeScript定义管理器,请执行以下操作

npm install typings --global

创建typings/custom/window.d.ts

interface Window {
  MyNamespace: any;
}

declare var window: Window;

安装您的自定义键入:

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

完成,使用它‌!打字稿不会再抱怨了:

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

1
FWIW typings被Typescript 2.0(2016年中)淘汰,并已由所有者存档。
mrm

13

打字稿不会对字符串属性执行类型检查。

window["newProperty"] = customObj;

理想情况下,应避免使用全局变量方案。我有时用它来调试浏览器控制台中的对象。




7

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

.d.ts定义文件中

type MyGlobalFunctionType = (name: string) => void

如果您在浏览器中工作,则通过重新打开Window的界面将成员添加到浏览器的window上下文中:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

NodeJS的想法相同:

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

现在,您声明根变量(实际上将存在于window或global上)

declare const myGlobalFunction: MyGlobalFunctionType;

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

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

最后,通过以下任一方式在代码库中的其他地方使用它:

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

myGlobalFunction("Kevin");

谢谢,这是我找到的最好的例子,并且运行良好。
尼克G.18年

6

创建一个自定义界面扩展Window并将您的自定义属性添加为可选属性。

然后,让customWindow使用自定义界面,但与原始窗口一起使用。

它与typescript@3.1.3一起使用。

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 

5

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

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

您可能会尝试做这样的事情

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

上面将错误。这是因为在Typescript中,接口不能很好地使用计算属性,并且会抛出类似

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

为了解决这个问题,你可以用暗示铸造去window<any>这样你就可以做

(window as any)[DynamicObject.key]

3
这就是2019
。– Qwerty19年

4

使用create-react-app v3.3,我发现实现此目的的最简单方法是扩展Window自动生成的类型react-app-env.d.ts

interface Window {
    MyNamespace: any;
}

3

首先,您需要在当前范围内声明窗口对象。
因为打字稿想知道对象的类型。
由于窗口对象是在其他位置定义的,因此无法重新定义它。
但是您可以声明如下:

declare var window: any;

这不会重新定义window对象,也不会创建另一个具有name的变量window
这意味着窗口是在其他地方定义的,您只是在当前作用域中引用它。

然后,您可以通过以下方式简单地引用MyNamespace对象:

window.MyNamespace

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

window.MyNamespace = MyObject

现在打字稿将不会抱怨。


我可以知道您想知道在哪里window定义的用例吗?
Mav55

2

我今天想在Angular(6)库中使用它,花了我一段时间才能使它按预期工作。

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

所以最后,该文件最终显示为:

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

创建完成后,请不要忘记在您的中公开它public_api.ts

对我来说就做到了。希望这可以帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.