如何使用jsdom和typescript防止“属性'...'在'Global'类型上不存在”?

Posted

技术标签:

【中文标题】如何使用jsdom和typescript防止“属性\'...\'在\'Global\'类型上不存在”?【英文标题】:How to prevent "Property '...' does not exist on type 'Global'" with jsdom and typescript?如何使用jsdom和typescript防止“属性'...'在'Global'类型上不存在”? 【发布时间】:2017-04-06 04:52:56 【问题描述】:

我尝试将现有项目转换为使用 Typescript,但我的测试设置出现问题。

我有一个用于设置 jsdom 的测试设置文件,以便我的所有 DOM 交互代码在我的测试期间都可以正常工作。使用 Typescript(带有 mocha 的 ts-node)我总是得到这样的错误:

Property 'window' does not exist on type 'Global'.

为了防止这种情况,我尝试像这样修补 NodeJS.Global 接口:

declare namespace NodeJS
  interface Global 
    document: Document;
    window: Window;
    navigator: Navigator;
  

但这并没有改变任何东西。

如何在 NodeJS 全局变量上启用这些浏览器属性?

附加:

这是我的摩卡咖啡setup.ts:

import  jsdom, changeURL  from 'jsdom';

const exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('');
global.window = global.document.defaultView;
Object.keys(global.document.defaultView).forEach((property) => 
  if (typeof global[property] === 'undefined') 
    exposedProperties.push(property);
    global[property] = global.document.defaultView[property];
  
);

global.navigator = 
  userAgent: 'node.js',
;

changeURL(global.window, 'http://example.com/');

【问题讨论】:

不能确定这是正确的答案,但是经历了类似的痛苦,事实证明你扩展 NodeJS.Global 接口的文件必须全小写,否则打字会忽略它.此外,它的顶部不能有任何导入或导出语句 - 否则它将被视为模块而不是类型文件。 这些答案似乎都不起作用,有人在 2018 年有答案吗? 【参考方案1】:

避免错误的原始答案

把它放在你的打字稿文件的顶部

const globalAny:any = global;

然后使用 globalAny 代替。

globalAny.document = jsdom('');
globalAny.window = global.document.defaultView;

更新了维护类型安全的答案

如果您想保持您的类型安全,您可以扩充现有的 NodeJS.Global 类型定义。

您需要将您的定义放在全局范围内declare global ...

请记住,typescript global 范围与 NodeJS 接口 Global 或称为 global 类型 Global 的节点 global property 不同...

declare global 
  namespace NodeJS 
    interface Global 
       document: Document;
       window: Window;
       navigator: Navigator;
     
  

【讨论】:

通过转换 any 来删除类型确实违背了首先使用 Typescript 的原因。事实上,如果你正在 linting TSLint 有一个选项可以专门防止这种情况发生:palantir.github.io/tslint/rules/no-any @willsquire 我使用类型安全选项更新了解决方案。包括这对 OP 不起作用的主要原因。 此声明与不使用命名空间的 eslint 规则不兼容。看看我下面的答案,了解更有趣的方法。 问题与eslint无关,规则是个人风格问题。 ESLint 规则是个人的,真实的,但现在每个人通常都会使用所有默认规则作为基础,并禁用或修改这些他们不习惯的规则。我下面的解决方案涵盖了所有情况,IMO 它更具语义性,因此使答案更加完整。【参考方案2】:
declare namespace NodeJS 
  export interface Global  window: any;
  

【讨论】:

纯代码答案对 SO 读者的教育作用很小。请编辑您的答案以包括一些解释。您的答案在审核队列中,因为它已被标记为低质量。 试过你的答案,但 global.window 仍未解决【参考方案3】:

我通过这样做解决了这个问题...

export interface Global 
  document: Document;
  window: Window;


declare var global: Global;

【讨论】:

export interface Global extends NodeJS.Global 如果您想修改一些但保留 Global 的其余属性,例如 fetch【参考方案4】:

除了其他答案,你也可以直接在作业现场直接castglobal

(global as any).myvar = myvar;

【讨论】:

这与 typescript 的用法相矛盾,因为它是中立的类型检查,是 typescript 的第一个原则用法。 @ShahinGhasemi 真的,我到了一个我不在乎的地步。我已经厌倦了花费数小时甚至一整天来编译和运行我的应用程序。我曾经开发和生产,现在我花费越来越多的时间来解决打字错误、编译问题和依赖不兼容问题。我不在乎这是否违背了范式或违背了目的。在某个时候,我想要的只是我该死的应用程序编译并最终休息一下。这是唯一对我有用的解决方案。所有其他人只会导致新的错误和问题。【参考方案5】:

避免类型转换any,它消除了类型的目的。而是安装所需的类型定义(例如yarn add --dev @types/jsdom @types/node)并导入使用:

import  DOMWindow, JSDOM  from 'jsdom'

interface Global extends NodeJS.Global 
  window: DOMWindow,
  document: Document,
  navigator: 
    userAgent: string
  


const globalNode: Global = 
  window: window,
  document: window.document,
  navigator: 
    userAgent: 'node.js',
  ,
  ...global

【讨论】:

【参考方案6】:

这是正确的解决方案,不使用 Typescript 的命名空间。它还兼容所有 eslint 默认规则:

// Declare a type.
interface CustomNodeJsGlobal extends NodeJS.Global 
    myExtraGlobalVariable: number;
    // You can declare anything you need.

使用它:

// Tell Typescript to use this type on the globally scoped `global` variable.
declare const global: CustomNodeJsGlobal;

function doSomething() 
  // Use it freely
  global.myExtraGlobalVariable = 5;


doSomething();

【讨论】:

使用最新推荐的@typescript-eslint 规则,eslint: error @typescript-eslint/no-unsafe-member-access - Unsafe member access .Global on an any value. 可能会弹出。通过之前添加type NJSGlobal = NodeJS.Global; 并扩展NJSGlobal 来解决。除此之外,这个解决方案似乎是最好的方法。 最干净的答案。人们应该支持这个。【参考方案7】:

一个简单的方法可以使用扩展Typescript“Window”类型

第 1 步:附加 Window 对象

interface Window 
    foo:string // any type of your foo property

第 2 步:声明

let foo = 'value of foo'

第三步:添加到窗口对象

window.foo 

window.foo = foo

为我工作...

【讨论】:

以上是关于如何使用jsdom和typescript防止“属性'...'在'Global'类型上不存在”?的主要内容,如果未能解决你的问题,请参考以下文章

在TypeScript中如何防止属性被添加到一个空对象中?

如何防止 jquery 使用 webpack 和 typescript 导入两次?

如何防止 Vetur 和 TypeScript 在 VSCode 中同时显示打字稿警告?

微信公众号 过滤 typescript cheerio

如何防止使用 TypeScript 在构造函数中未定义成员?

JSDOM获取子节点的一些方法