Как предотвратить "свойство" не существует для типа "Global" с jsdom и typescript?

Я пытаюсь преобразовать существующий проект для использования Typescript, и у меня возникают проблемы с настройкой тестирования.

У меня был установочный файл для моих тестов, который настраивает jsdom так, чтобы весь мой код взаимодействия с DOM работал во время моих тестов. Используя Typescript (ts-node с mocha), я всегда получаю такие ошибки:

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/');

person fahrradflucht    schedule 22.11.2016    source источник
comment
Не могу быть уверен, что это правильный ответ, но, пройдя через подобную боль, выяснилось, что файл, в котором вы расширяете интерфейс NodeJS.Global, должен быть полностью в нижнем регистре, иначе набор текста проигнорирует его. Кроме того, он не может иметь никаких операторов импорта или экспорта вверху - иначе он будет рассматриваться как модуль, а не как файл типизации.   -  person Izhaki    schedule 15.12.2016
comment
ни один из этих ответов, похоже, не работает, есть ли у кого-нибудь ответ в 2018 году?   -  person Alexander Mills    schedule 15.03.2018


Ответы (7)


Оригинальный ответ, чтобы избежать ошибки

Поместите это в начало файла машинописного текста

const globalAny:any = global;

Тогда используйте вместо этого globalAny.

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

Обновленный ответ для обеспечения безопасности типов

Если вы хотите сохранить безопасность вашего типа, вы можете расширить существующее определение типа NodeJS.Global.

Вам нужно поместить свое определение в глобальную область declare global {...}

Имейте в виду, что область действия машинописного текста global отличается от интерфейса NodeJS Global или узла global property, называемого global типа _9 _...

declare global {
  namespace NodeJS {
    interface Global {
       document: Document;
       window: Window;
       navigator: Navigator;
    } 
  }
}
person Steven Spungin    schedule 17.02.2017
comment
Удаление типизации путем преобразования any действительно противоречит тому, почему в первую очередь используется Typescript. Фактически, если вы выполняете линтинг, у TSLint есть опция, специально предназначенная для предотвращения этого: palantir.github.io / tslint / rules / no-any - person Will Squire; 21.01.2019
comment
@willsquire Я обновил решение, добавив в него типобезопасный вариант. Включена основная причина, по которой это не сработало для OP. - person Steven Spungin; 24.06.2019
comment
Это объявление несовместимо с правилом eslint о неиспользовании пространств имен. Посмотрите на мой ответ ниже, чтобы узнать о более интересном подходе. - person José Cabo; 14.12.2019
comment
Вопрос не имеет ничего общего с eslint, и правила - это вопрос личного стиля. - person Steven Spungin; 14.12.2019
comment
Правила ESLint носят индивидуальный характер, правда, но в настоящее время каждый обычно использует все правила по умолчанию в качестве основы и отключает или изменяет их, чем им неудобно. Мое решение ниже охватывает все случаи, и ИМО оно более семантическое, поэтому ответ более полный. - person José Cabo; 04.02.2020
comment
это должно быть: globalAny.window = globalAny.document.defaultView в противном случае вы снова получите ошибку машинописного текста - person Greg Wozniak; 14.05.2020
comment
Использование обновленного ответа дает: error TS2304: Cannot find name 'Document'. Cannot find name 'Window'. Cannot find name 'Navigator'. - person Jeremy Thille; 22.06.2021
comment
@JeremyThille звучит как проблема с включением библиотеки в конфигурацию машинописного текста. Все они есть в библиотеке Dom. - person Steven Spungin; 22.06.2021

В дополнение к другим ответам вы также можете просто преобразовать global напрямую на месте назначения:

(global as any).myvar = myvar;
person lleaff    schedule 29.07.2018
comment
Это противоречит использованию машинописного текста, поскольку это нейтральная проверка типов, первый принцип использования машинописного текста. - person Shahin Ghasemi; 11.06.2020
comment
@ShahinGhasemi На самом деле, я дошел до того, что мне все равно. Я так устал тратить часы и даже целые дни только на то, чтобы мои приложения компилировались и запускались. Раньше я занимался разработкой и производством, теперь я трачу все больше и больше дней на решение опечаток, проблем с компиляцией и несовместимости зависимостей. Меня не волнует, противоречит ли это парадигме или противоречит цели. Все, что я хочу в какой-то момент, - это скомпилировать свое чертово приложение и, наконец, немного отдохнуть. Это единственное решение, которое у меня сработало. Все остальные просто приводят к новым ошибкам и проблемам. - person Jeremy Thille; 22.06.2021

Я исправил эту проблему, сделав это ...

export interface Global {
  document: Document;
  window: Window;
}

declare var global: Global;
person Shawn    schedule 05.10.2017
comment
export interface Global extends NodeJS.Global, если вы хотите изменить некоторые, но сохранить остальные свойства Global, например fetch - person casey; 01.06.2018

Избегайте приведения типов 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
}
person Will Squire    schedule 20.01.2019

Это правильное решение, не использующее пространства имен 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();
person José Cabo    schedule 14.12.2019
comment
При использовании новейших рекомендованных правил @ typescript-eslint может появиться eslint: error @typescript-eslint/no-unsafe-member-access - Unsafe member access .Global on an any value.. Решено добавлением type NJSGlobal = NodeJS.Global; ранее и расширением NJSGlobal. Помимо этого, это решение кажется лучшим выходом. - person Zweihänder; 23.05.2020
comment
Самый чистый ответ. Люди должны проголосовать за это. - person devuxer; 06.03.2021

Можно использовать простой метод для расширения типа окна Typescript

шаг 1: добавить объект Window

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

шаг 2: объявить

let foo = 'value of foo'

шаг 3: добавить объект в окно

window.foo 

or

window.foo = foo

работать на меня ...

person Ali Virk    schedule 29.04.2021

person    schedule
comment
Ответы, содержащие только код, мало что могут сделать для обучения читателей SO. Измените свой ответ, включив в него некоторые пояснения. Ваш ответ находится в очереди на модерацию, поскольку был отмечен как некачественный. - person mickmackusa; 19.04.2017
comment
Пробовал ваш ответ, но global.window все еще не решен - person Mick; 18.06.2018