Wie kann verhindert werden, dass "Eigenschaft" ... "für den Typ" Global "mit jsdom und typescript nicht vorhanden ist?

72

Ich versuche, ein vorhandenes Projekt für die Verwendung von Typescript zu konvertieren, und habe Probleme mit meinem Test-Setup.

Ich hatte eine Setup-Datei für meine Tests, die jsdom so einrichtet, dass mein gesamter DOM-Interaktionscode während meiner Tests funktioniert. Mit Typescript (ts-Knoten mit Mokka) erhalte ich immer folgende Fehler:

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

Um dies zu verhindern, habe ich versucht, die NodeJS.Global-Oberfläche wie folgt zu patchen:

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

Daran hat sich aber nichts geändert.

Wie aktiviere ich diese Browsereigenschaften für die globale NodeJS-Variable?

Extras:

Das ist mein Mokka 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/');
fahrradflucht
quelle
Ich kann nicht sicher sein, ob es die richtige Antwort ist, aber nachdem ich ähnliche Probleme hatte, stellt sich heraus, dass die Datei, in der Sie die NodeJS.Global-Oberfläche erweitern, nur in Kleinbuchstaben geschrieben sein muss. Andernfalls wird sie durch Eingabe ignoriert. Außerdem kann es oben keine Import- oder Exportanweisungen geben - oder es wird eher als Modul als als Typisierungsdatei angesehen.
Izhaki
4
Keine dieser Antworten scheint zu funktionieren. Hat jemand 2018 eine Antwort?
Alexander Mills

Antworten:

103

Ursprüngliche Antwort, um Fehler zu vermeiden

Fügen Sie dies oben in Ihre Typoskriptdatei ein

const globalAny:any = global;

Verwenden Sie stattdessen globalAny.

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

Aktualisierte Antwort zur Aufrechterhaltung der Typensicherheit

Wenn Sie Ihre Typensicherheit beibehalten möchten, können Sie die vorhandene NodeJS.GlobalTypdefinition erweitern.

Sie müssen Ihre Definition in den globalen Bereich einfügen declare global {...}

globalBeachten Sie, dass der Typoskriptbereich nicht mit der NodeJS-Schnittstelle Globaloder dem vom Typ global propertyaufgerufenen Knoten identisch ist ...globalGlobal

declare global {
  namespace NodeJS {
    interface Global {
       document: Document;
       window: Window;
       navigator: Navigator;
    } 
  }
}
Steven Spungin
quelle
10
Das Entfernen von Typisierungen durch Casting widerspricht anywirklich der Frage, warum Typescript überhaupt verwendet wird. Infact, wenn Sie fusseln TSLint hat eine Option speziell, um dies zu verhindern: palantir.github.io/tslint/rules/no-any
Will Squire
1
@willsquire Ich habe die Lösung mit einer typsicheren Option aktualisiert. Der Hauptgrund, warum dies für OP nicht funktioniert hat, ist enthalten.
Steven Spungin
2
Diese Deklaration ist nicht mit der Eslint-Regel kompatibel, keine Namespaces zu verwenden. Schauen Sie sich meine Antwort unten an, um einen interessanteren Ansatz zu finden.
José Cabo
Die Frage hat nichts mit Eslint zu tun, und Regeln sind eine Frage des persönlichen Stils.
Steven Spungin
2
ESLint-Regeln sind zwar persönlich, aber heutzutage verwendet normalerweise jeder alle Standardregeln als Basis und deaktiviert oder ändert diese, wenn er nicht damit vertraut ist. Meine Lösung unten deckt alle Fälle ab und IMO ist semantischer, wodurch die Antwort vollständiger wird.
José Cabo
38

Neben anderen Antworten können Sie auch einfach direkt am Einsatzort gießen global :

(global as any).myvar = myvar;
lleaff
quelle
2
Dies steht im Widerspruch zur Verwendung von Typoskripten, da es die neutrale Typprüfung ist, die erste prinzipielle Verwendung des Typoskripts.
Shahin Ghasemi
19

Ich habe dieses Problem dadurch behoben ...

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

declare var global: Global;
Shawn
quelle
7
export interface Global extends NodeJS.GlobalWenn Sie einige ändern möchten, aber den Rest der Eigenschaften von Global fetch
beibehalten
8

Vermeiden anySie Typografie, da dies den Zweck der Typisierung aufhebt. Installieren Sie stattdessen die erforderlichen Typdefinitionen (z. B. yarn add --dev @types/jsdom @types/node) und importieren Sie sie, um Folgendes zu verwenden:

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
}
Will Squire
quelle
3

Dies ist die richtige Lösung, ohne die Namespaces von Typescript zu verwenden. Es ist auch mit allen Standardregeln von eslint kompatibel:

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

Benutze es:

// 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();
José Cabo
quelle
Mit den neuesten empfohlenen @ typescript-eslint-Regeln wird eslint: error @typescript-eslint/no-unsafe-member-access - Unsafe member access .Global on an any value.möglicherweise angezeigt. Gelöst durch type NJSGlobal = NodeJS.Global;vorheriges Hinzufügen und Erweitern NJSGlobal. Darüber hinaus scheint diese Lösung der beste Weg zu sein.
Zweihänder
1
declare namespace NodeJS {
  export interface Global { window: any;
  }
}
Oleg Mikhailenko
quelle
24
Nur-Code-Antworten tragen kaum dazu bei, SO-Leser zu unterrichten. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung zu erhalten. Ihre Antwort befindet sich in der Moderationswarteschlange, da sie als minderwertig gekennzeichnet wurde.
Mickmackusa
Versuchte Ihre Antwort, aber global.window ist noch ungelöst
Mick