ChatGPT解决这个技术问题 Extra ChatGPT

你如何在 TypeScript 的“window”上显式设置一个新属性?

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

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

TypeScript 强调 MyNamespace 并抱怨:

“窗口”类型的值上不存在属性“MyNamespace”任何“

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

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

我怎样才能保留 window 并让 TypeScript 满意?

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

一些答案是 a meta question 的主题。

P
Peter Mortensen

我刚刚在 another Stack Overflow question's answer 中找到了答案。

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

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

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


注意 Window 中的大写 W。那把我绊倒了。
请注意,这仅在单独的 .d.ts 文件中声明时才有效。在使用导入和导出本身的 .ts 文件中使用时,它不起作用。请参阅this answer
declare global { interface Window { ... } } 与 TypeScript 2.5.2 一起使用,不需要上面提到的 .d.ts 文件
declare interface Window { __GLOBAL_VAR__: any }
如果您收到错误“TS2339:属性 'MyNamespace' 在类型 'Window & typeof globalThis' 上不存在。”然后在文件开头添加 export {}
M
MT0

要保持动态,只需使用:

(<any>window).MyNamespace

请注意,这可能不适用于 TSX,因为编译器可能认为 <any> 是 TSX 元素。查看 this answer 以了解与 TSX 兼容的类型断言。


任何想法如何在 tsx 文件中执行此操作?
@martin:<any>让它明确,所以它工作得很好,我相信。其他:如果您将 Typescript 与 React 或其他 JSX 环境(导致 TSX 语法)一起使用,则必须使用 as 而不是 <>。请参阅 @david-boyd 的 answer below
我不得不使用 (window as any).MyNamespace
this - return (<any> this)['something in scope'] 也是如此
我不得不使用 /* tslint:disable */ 在该行禁用 tslint
m
mikemaccana

使用 Svelte 还是 TSX?其他答案都不适合我。

这是我所做的:

(window as any).MyNamespace

实际上与 (<any> window).MyNamespace 相同
除了使用 TSX 时,它是相同的,因为 <any> 被解释为 JSX,而不是类型转换。
感谢@David,这对新的 Create React App 和 typescript 版本来说就像一个魅力,以前这是有效的:(window).MyNamespace,但现在它正在打破新的 typescript 版本 3.5.x。
窗口一样吗?那你为什么要使用打字稿?只需使用 Javascript x)
有趣的是人们如何使用 Typescript 然后将其静音。这和根本不使用它一样好。并认为这些答案是最受欢迎的答案!听起来很有希望...
P
Peter Mortensen

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

或者...

你可以输入:

window['MyNamespace']

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


但你可能会得到一个 tslint 错误......如果你当然有一个
这完全违背了强类型化,即 TypeScript 背后的整个理念。
@user1334007 使用全局变量也是如此。但是,一些遗留代码需要它。
^3.4.3 中不起作用。 TypeScript error: Element implicitly has an 'any' type because type 'Window' has no index signature. TS7017
我真的必须回应@Auspex。 TypeScript 不是您应该尝试解决的问题。如果您使用的是 TS,请接受它并选择正确的类型。如果您不想这样做,请不要使用 TypeScript。如果您被迫使用它但又不想这样做,请向您的经理提出从 TS 切换的案例,或者寻求帮助以正确输入。将 TS 代码库作为强/静态类型的被动攻击性滑动来混淆和破坏 TS 代码库是非常不专业和任性的。
P
Peter Mortensen

全局变量是“邪恶的” :) 我认为获得可移植性的最佳方法是:

首先你导出界面:(例如,./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

二、你进口

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

三、用CustomWindow强制转换全局变量window:

declare let window: CustomWindow;

这样,如果您将它与窗口对象的现有属性一起使用,则在不同的 IDE 中也没有红线,所以最后尝试:

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

使用 TypeScript 2.4.x 和最新版本进行测试!


您可能想扩展为什么全局变量是邪恶的。在我们的例子中,我们在窗口对象上使用了一个非标准化的 API。它现在是 polyfill 的。那真的是邪恶的吗?
@MathijsSegers我特别不知道你的情况,但是..关于全局变量的邪恶不仅仅是关于javascript..在每种语言中..一些原因是: - 你不能很好地应用设计模式 - 内存和性能问题(你让它们无处不在,尝试将一些东西附加到字符串对象,看看当你做新字符串时会发生什么) - 名称冲突导致代码的副作用..(特别是在具有异步性质的 javascript 上)我可以继续,但我认为你可以想象休息...
@onabi 我实际上是在谈论浏览器的一个功能。它是全球可用的,因为它是浏览器。您真的会建议使用全局定义仍然是错误的吗?我的意思是我们可以实现 Ponyfills 但这会使实际上支持该功能的浏览器膨胀(例如全屏 api)。也就是说,我不相信所有的全球人都是邪恶的。
@MathijsSegers 在我看来,应该避免在我们可以使用或修改的地方使用或修改全局变量.. 确实,由于浏览器存在,窗口是可用的,但也确实一直处于突变状态.. 所以如果我们现在定义window.feature = '功能';这在代码上被大量使用..如果浏览器在所有代码上添加了window.feature会发生什么,这个功能被覆盖..无论如何我解释了我的句子不是为了反对你..问候...
当然,根据用例,我也更喜欢这种方法,因为它允许我键入非标准 API,而无需在代码库中使用它。此外,如果您正在开发一个库,您可能不想更改全局范围,因为您的用户可能会根据这些类型的存在而开始。
P
Peter Mortensen

对于那些使用 Angular CLI 的人来说,这很简单:

文件 src/polyfills.ts

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

文件 my-custom-utils.ts

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

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

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

declare global 是诀窍,这个答案并不是真正特定于 Angular CLI ......
B
Blake Mitchell

接受的答案是我以前使用的答案,但是使用 TypeScript 0.9.* 它不再有效。 Window 接口的新定义似乎完全取代了内置定义,而不是对其进行扩充。

我已经开始这样做了:

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

declare var window: MyWindow;

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


这也适用于 TypeScript 2.0.8 使用的模块。示例:export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() 然后您可以调用 foo() 例如从 Chrome 开发工具控制台,如 mc.foo()
如果您不想将某些东西声明为全局,这是一个非常好的答案。另一方面,您需要在所需的每个文件中调用 declare var...
对于这种方法加一个,因为在这种情况下,您不会与扩展 monorepo 中的全局窗口的其他包发生冲突。
谢谢!最佳答案海事组织。不应该覆盖 Window,因为它不是一个普通的窗口。仅仅规避类型检查或试图欺骗 TS 也不是解决问题的方法。
P
Peter Mortensen

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

窗口.d.ts

import MyInterface from './MyInterface';

declare global {
    interface Window {
        propName: MyInterface
    }
}

请参阅手册“声明合并”部分中的Global Augmentation


P
Peter Mortensen

创建一个名为 global.d.ts 的文件,例如 /src/@types/global.d.ts,然后定义如下接口:

interface Window {
  myLib: any
}

参考:Global .d.ts


P
Peter Mortensen

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

其中一些只是抑制了 show 的类型推断。

其他一些只关心全局变量作为命名空间,而不是接口/类

今天早上我也遇到了类似的问题。我在 Stack Overflow 上尝试了很多“解决方案”,但没有一个绝对不会产生类型错误并在 IDE(WebStormVisual Studio Code)中启用触发类型跳转。

最后,来自 Allow module definitions to be declared as global variables #3180

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

示例如下:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

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


P
Peter Mortensen

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

我只是做了:

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

或者你可以这样做:

let myWindow = window as any;

然后myWindow.myProp = 'my value';


在这种情况下,您还可以安装 npm 包,其中包含所有定义 - as specified in the docs
您可以这样做,但这不是问题的答案,而是一种解决方法
多亏了这一点,我可以通过使用 (window as any).__REDUX_DEVTOOLS_EXTENSION__ && (window as any).__REDUX_DEVTOOLS_EXTENSION__()) 在 typescript 下正确启用 redux 工具
P
Peter Mortensen

在找到答案后,我认为此页面可能会有所帮助:

Global augmentation

我不确定声明合并的历史,但它解释了为什么以下可以工作。

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

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

P
Peter Mortensen

从 3.4 版开始,TypeScript 已支持 globalThis。请参阅 Type-checking for globalThis

从上面的链接:

// 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

“全局”文件是没有任何导入/导出语句的文件。所以声明 var abc; 可以写在 .d.ts 中。


是的,但它是否在窗口对象上声明了变量,这是提出的问题?我非常怀疑
确实如此。 lib.d.tswindow 声明为类型 Windows & globalThis,这意味着这两种类型合并在一起。
K
Kris Dover

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

interface Window {
    MyNamespace: any;
}

P
Peter Mortensen

TypeScript 不对字符串属性执行类型检查。

window["newProperty"] = customObj;

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


P
Peter Mortensen

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

interface Window {
  someValue: string
  another: boolean
}

这在使用 TypeScript 3.3、WebpackTSLint 时对我有用。


^3.4.3 中不起作用。这个答案对我有用stackoverflow.com/a/42841166/1114926
“自 2019 年起,TSLint 已被弃用。有关详细信息,请参阅此问题:路线图:TSLint → ESLint。”Source
P
Peter Mortensen

如果您使用的是 TypeScript Definition Manager,请按照以下步骤操作!

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

完毕!用它! TypeScript 不会再抱怨了:

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

FWIW typings 已在 Typescript 2.0(2016 年中)中过时,并已由所有者存档。
P
Peter Mortensen

使用

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

应该没问题,因为它使用的是字符串属性,但是如果你真的想要一个单独的窗口并组织你的代码,你可以扩展窗口对象:

interface MyNamespacedWindow extends Window {
    MyNamespace: object;
}

declare var window: MyNamespacedWindow;

我喜欢您的第二个解决方案,但它会在 declare 上抛出一个 TS 错误,说 Modifiers cannot appear here. ts(1184)。有任何想法吗?
P
Peter Mortensen

首先,您需要在当前范围内声明窗口对象。因为 TypeScript 想知道对象的类型。由于窗口对象是在其他地方定义的,因此您不能重新定义它。

但是你可以声明如下:

declare var window: any;

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

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

window.MyNamespace

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

window.MyNamespace = MyObject

现在 TypeScript 不会抱怨了。


P
Peter Mortensen

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

.d.ts 定义文件中

type MyGlobalFunctionType = (name: string) => void

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

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Node.js 的想法相同:

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

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

declare const myGlobalFunction: MyGlobalFunctionType;

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

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

最后在代码库的其他地方使用它,其中之一是:

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

myGlobalFunction("Kevin");

P
Peter Mortensen

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

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

它适用于 TypeScript 3.1.3。

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {}

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

https://i.stack.imgur.com/9DPDK.png


这对我来说效果很好,并且感觉是一种在适用的情况下全局定义类型的明确方式。我将补充一点,就我而言,我想添加具有导入类型的 Cypressapp。通过从各自的库中导入这两种类型并相应地声明属性,我能够很好地实现这一点:``` import { Cypress } from 'cypress';从'vue'导入{ ComponentPublicInstance }; // 将 Cypress 和应用程序添加到全局窗口以进行测试 export declare global { interface Window { Cypress: Cypress;应用程序:组件公共实例; } } ```
P
Peter Mortensen

对于那些想要在 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]

M
Masih Jahangiri

完整和简短的答案

1-将 typeRoots 属性添加到 tsconfig.json:

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

2- 扩展窗口类型

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

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

U
Up209d
(window as { test: string } & Window & typeof globalThis).test = `Hello World`;

P
Peter Mortensen

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

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

所以最后,该文件最终得到如下内容:

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

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

那是为我做的。


P
Peter Mortensen

TypeScript 会阻止在未分配具有所需属性或已分配给 any 的类型的情况下访问对象,因此您可以使用可选链接:

window?.MyNamespace =  'value'

这不会删除打字稿警告。
b
bob

我使用没有声明。我的示例如下所示:

https://i.stack.imgur.com/qG8yc.png


N
Narendra Maurya

[2022]:我们必须在 React 或 Nextjs 项目中扩展“窗口”对象。我们可以使用以下步骤来解决这个问题。

在 src 文件夹名称中创建一个文件夹作为类型。

在类型文件夹名称中创建一个文件为 index.d.ts

将此代码写入 index.d.ts 文件中。出口 {};声明全局 { interface Window { NameSpace: any; } } 窗口.名称空间=窗口.名称空间|| {};

https://i.stack.imgur.com/Shpiv.png

现在最后一个变化。

更改“tsConfig.json”文件。继承节点模块类型和我们的类型。

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

https://i.stack.imgur.com/Pyf4N.png