Wie setzen Sie in TypeScript explizit eine neue Eigenschaft für `window`?

621

Ich richte globale Namespaces für meine Objekte ein, indem ich explizit eine Eigenschaft auf setze window.

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

TypeScript unterstreicht MyNamespaceund beschwert sich über:

Die Eigenschaft 'MyNamespace' ist für den Wert vom Typ 'window' any "nicht vorhanden.

Ich kann den Code MyNamespacezum Laufen bringen, indem windowich ihn als Umgebungsvariable deklariere und die explizite Aussage lösche, aber das möchte ich nicht.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Wie kann ich dort bleiben windowund TypeScript glücklich machen?

Als Randnotiz finde ich es besonders lustig, dass sich TypeScript beschwert, da es mir sagt, dass windowes sich um einen Typ handelt, anyder definitiv alles enthalten kann.

Joshuapoehls
quelle
siehe Link
MHS

Antworten:

692

Ich habe die Antwort auf diese Frage in der Antwort einer anderen StackOverflow-Frage gefunden .

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

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

Grundsätzlich müssen Sie die vorhandene windowSchnittstelle erweitern, um Informationen zu Ihrer neuen Eigenschaft zu erhalten.

Joshuapoehls
quelle
12
Beachten Sie das Großbuchstaben W im Fenster. Das hat mich gestolpert.
Ajm
2
Ich konnte dies nicht mit tsc 1.0.1.0 kompilieren. Die Antwort von Blake Mitchell hat jedoch für mich funktioniert.
Pat
43
Beachten Sie, dass dies nur funktioniert, wenn es in einer separaten .d.tsDatei deklariert ist . Es funktioniert nicht, wenn es in einer .tsDatei verwendet wird, die Importe und Exporte selbst verwendet. Siehe diese Antwort .
cdauth
36
Das declare global { interface Window { ... } }funktioniert mit TypeScript 2.5.2, keine Notwendigkeit für .d.tsDatei wie oben erwähnt
tanguy_k
2
Das erste Typoskript sagte mir: error TS1234: An ambient module declaration is only allowed at the top level in a file.Als ich es oben in die Datei einfügte, wurde dieser Fehler behoben, sodass Sie dies möglicherweise auch tun müssen.
Zelphir Kaltstahl
397

Um es dynamisch zu halten, verwenden Sie einfach:

(<any>window).MyNamespace
Chinupson
quelle
9
Irgendwelche Ideen, wie man das in einer tsx-Datei macht?
Velop
2
@martin: Das <any> macht es explizit, also funktioniert es gut, glaube ich. Andere: Wenn Sie Typescript mit React oder einer anderen JSX-Umgebung verwenden (was zu einer TSX-Syntax führt), müssen Sie asstattdessen verwenden <>. Siehe die Antwort von @ david-boyd unten .
Don
31
Ich musste (Fenster wie jedes) verwenden .MyNamespace
Arsalan Ahmad
Ähnlich für this-return (<any> this)['something in scope']
HankCa
1
Ich musste tslint auf dieser Linie mit/* tslint:disable */
Michael Yagudaev
197

AB TYPESCRIPT ^ 3.4.3 DIESE LÖSUNG FUNKTIONIERT NICHT MEHR

Oder...

Sie können einfach Folgendes eingeben:

window['MyNamespace']

und Sie erhalten keinen Kompilierungsfehler und es funktioniert genauso wie beim Tippen window.MyNamespace

Evan Larsen
quelle
15
aber Sie werden wahrscheinlich einen tslint-Fehler bekommen ... Wenn Sie natürlich einen haben
smnbbrv
69
Dies widerspricht völlig der starken Eingabe, der ganzen Idee hinter TypeScript.
d512
18
@ user1334007 mit Globals auch. Einige Legacy-Codes erfordern dies jedoch.
Nathancahill
3
@ Ramesh window['MyNamespace']()(nur Klammern hinzufügen)
iBaff
6
"Dies steht im Widerspruch zu starkem Tippen, der ganzen Idee hinter TypeScript." GUT!
Cody
188

Verwenden Sie TSX? Keine der anderen Antworten funktionierte für mich.

Folgendes habe ich getan:

(window as any).MyNamespace
David Boyd
quelle
4
Gleich wie (<any> window).MyNamespaceeigentlich
Dmitry Parzhitsky
44
Es ist dasselbe, außer wenn TSX verwendet wird, da das <any>als JSX interpretiert wird, nicht als Typumwandlung.
Jake Boone
1
Vielen Dank an @David, dies funktionierte wie ein Zauber mit der neuen Create React App und der Typoskript-Version. Früher funktionierte dies: (<jedes> Fenster) .MyNamespace, aber jetzt wird die neue Typoskript-Version 3.5.x eingebrochen.
Jignesh Raval
Fenster wie jedes? Warum sollten Sie dann Typoskript verwenden? Verwenden Sie einfach Javascript x)
MarcoLe
71

Die akzeptierte Antwort ist die, die ich früher verwendet habe, aber mit TypeScript 0.9. * Funktioniert sie nicht mehr. Die neue Definition der WindowSchnittstelle scheint die integrierte Definition vollständig zu ersetzen, anstatt sie zu erweitern.

Ich habe mich stattdessen dazu entschlossen:

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

declare var window: MyWindow;

UPDATE: Mit TypeScript 0.9.5 funktioniert die akzeptierte Antwort wieder.

Blake Mitchell
quelle
2
Dies funktioniert auch mit Modulen, wie sie von TypeScript 2.0.8 verwendet werden. Beispiel: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() Dann können Sie foo () zB von der Chrome Dev Tools-Konsole aus wie mc.foo()
Martin Majewski
Dies ist eine sehr schöne Antwort, wenn Sie etwas nicht als global deklarieren möchten. Auf der anderen Seite müssen Sie declare var...jede Datei aufrufen, die Sie benötigen.
Puce
Plus eins für diesen Ansatz, da Sie in diesem Fall nicht mit den anderen Paketen in Konflikt stehen, die das globale Fenster in der Monorepo erweitern.
Dmitrii Sorin
Vielen Dank! Beste Antwort IMO. Man sollte nicht Windoweinfach die Tatsache außer Kraft setzen , dass es sich nicht um ein Vanillefenster handelt. Nur die Typprüfung zu umgehen oder zu versuchen, TS zu täuschen, ist auch nicht der richtige Weg.
Rutger Willems
Dies funktioniert leider nicht ab TS 3.6
Jacob Soderlund
65

Global sind "böse" :), ich denke, der beste Weg, um auch die Portabilität zu haben, ist:

Zuerst exportieren Sie die Schnittstelle: (zB: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Zweitens importieren Sie

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

Drittes globales Var-Fenster mit CustomWindow

declare let window: CustomWindow;

Auf diese Weise haben Sie auch keine rote Linie in einer anderen IDE, wenn Sie mit vorhandenen Attributen des Fensterobjekts verwenden. Versuchen Sie also am Ende:

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

Getestet mit Typescript 2.4.x und neuestem!

onalbi
quelle
1
Vielleicht möchten Sie näher erläutern, warum Globals böse sind. In unserem Fall verwenden wir eine nicht standardisierte API für das Fensterobjekt. Es ist vorerst polygefüllt. Ist das wirklich böse?
Mathijs Segers
1
@MathijsSegers Ich kenne nicht besonders Ihren Fall, aber .. über Globals sind böse ist nicht nur über Javascript .. ist in jeder Sprache .. einige Gründe sind: - Sie können nicht gut Design-Muster anwenden - Speicher- und Leistungsprobleme (Sie Haben Sie sie überall, versuchen Sie, etwas an das String-Objekt anzuhängen, und sehen Sie, was passiert, wenn Sie einen neuen String ausführen.) - Namenskollision, die Nebenwirkungen auf den Code verursacht Ruhe ...
Onalbi
1
@onabi Ich spreche buchstäblich über eine Funktion des Browsers. Es ist global verfügbar, da es der Browser ist. Sie würden wirklich vorschlagen, dass es immer noch falsch ist, globale Definitionen zu wählen? Ich meine, wir könnten Ponyfills implementieren, aber dies würde Browser aufblähen, die die Funktion tatsächlich unterstützen (z. B. Vollbild-API). Das heißt, ich glaube nicht, dass alle Globalen böse Perse sind.
Mathijs Segers
2
@MathijsSegers Meiner Meinung nach sollte vermieden werden, dass die globale Variable verwendet oder geändert wird, wo wir können. Es ist wahr, dass das Fenster verfügbar ist, da ein Browser vorhanden ist, aber es ist auch wahr, dass es die ganze Zeit in Mutation war. Wenn wir also zum Beispiel jetzt definieren window.feature = 'Feature'; und dies wird massiv für Code verwendet. Was passiert, wenn window.feature von Browsern für den gesamten Code hinzugefügt wird? Diese Funktion wird überschrieben. Trotzdem habe ich eine Erklärung gegeben, dass mein Satz nicht gegen Sie verstoßen soll. Grüße ...
Onalbi
48

Wenn Sie das windowObjekt mit einem benutzerdefinierten Typ erweitern müssen, für den die Verwendung erforderlich ist import, können Sie die folgende Methode verwenden:

window.d.ts

import MyInterface from './MyInterface';

declare global {
    interface Window {
        propName: MyInterface
    }
}

Siehe 'Globale Erweiterung' im Abschnitt 'Zusammenführen von Erklärungen' des Handbuchs: https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation

Luzifer Sam
quelle
43

Für diejenigen, die die Angular CLI verwenden , ist dies unkompliziert:

src / polyfills.ts

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

my-custom-utils.ts

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

Wenn Sie IntelliJ verwenden, müssen Sie außerdem die folgende Einstellung in der IDE ändern, bevor Ihre neuen Polyfills aufgenommen werden:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.
Stephen Paul
quelle
1
declare globalist der Trick, und diese Antwort ist nicht wirklich spezifisch für Angular CLI ...
Avindra Goolcharan
31

Ich muss das nicht sehr oft tun. Der einzige Fall, den ich hatte, war die Verwendung von Redux Devtools mit Middleware.

Ich habe einfach getan:

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

Oder Sie könnten tun:

let myWindow = window as any;

und dann myWindow.myProp = 'my value';

Dimitar Nikovski
quelle
1
In diesem Fall können Sie auch das npm-Paket installieren, das alle Definitionen enthält - wie in den Dokumenten angegeben
bbrinx
Sie können dies tun, aber das ist nicht die Antwort auf die Frage, sondern eine Problemumgehung
Vitalij
Dank dessen konnte ich Redux-Tools unter Typoskript ordnungsgemäß aktivieren, indem ich (Fenster wie jedes) .__ REDUX_DEVTOOLS_EXTENSION__ && (Fenster wie jedes) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario
20

Die meisten anderen Antworten sind nicht perfekt.

  • Einige von ihnen unterdrücken nur die Typinferenz für den Shop.
  • Einige der anderen kümmern sich nur um globale Variablen als Namespace, nicht aber als Schnittstelle / Klasse

Ich stoße heute Morgen auch auf ein ähnliches Problem. Ich habe so viele "Lösungen" auf SO ausprobiert, aber keine von ihnen erzeugt absolut keinen Typfehler und ermöglicht das Auslösen des Typenspringens in der IDE (Webstorm oder Vscode).

Endlich von hier

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

Ich finde eine vernünftige Lösung, um Typisierungen für globale Variablen anzuhängen, die sowohl als Schnittstelle / Klasse als auch als Namespace fungieren .

Beispiel ist unten:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Der obige Code führt nun die Typisierungen von Namespace MyNamespaceund Schnittstelle MyNamespacein die globale Variable myNamespace(die Eigenschaft von window) ein.

E-Cloud
quelle
2
Vielen Dank - dies ist die einzige, die Sie scheinbar in einem nicht modularen Umgebungskontext verwenden können.
Tyler Sebastian
13

So geht's, wenn Sie TypeScript Definition Manager verwenden !

npm install typings --global

Erstellen typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Installieren Sie Ihre benutzerdefinierte Eingabe:

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

Fertig, benutze es ! Typoskript wird sich nicht mehr beschweren:

window.MyNamespace = window.MyNamespace || {};
Nik Sumeiko
quelle
1
FWIW typingswurde mit Typescript 2.0 (Mitte 2016) überholt und vom Eigentümer archiviert.
Herr
13

Typscript führt keine Typprüfung für Zeichenfolgeneigenschaften durch.

window["newProperty"] = customObj;

Idealerweise sollte das globale Variablenszenario vermieden werden. Ich benutze es manchmal, um ein Objekt in der Browserkonsole zu debuggen.

Irshad
quelle
8

Wenn Sie Typescript 3.x verwenden, können Sie möglicherweise den declare globalTeil in den anderen Antworten weglassen und stattdessen einfach Folgendes verwenden:

interface Window {
  someValue: string
  another: boolean
}

Dies funktionierte bei mir bei Verwendung von Typescript 3.3, WebPack und TSLint.

Dana Woodman
quelle
Funktioniert nicht in ^3.4.3. Diese Antwort funktionierte für mich stackoverflow.com/a/42841166/1114926
Green
7

Als Referenz (dies ist die richtige Antwort):

In einer .d.tsDefinitionsdatei

type MyGlobalFunctionType = (name: string) => void

Wenn Sie im Browser arbeiten, fügen Sie dem Fensterkontext des Browsers Mitglieder hinzu, indem Sie die Fensteroberfläche erneut öffnen:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Gleiche Idee für NodeJS:

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

Jetzt deklarieren Sie die Stammvariable (die tatsächlich im Fenster oder global lebt).

declare const myGlobalFunction: MyGlobalFunctionType;

Dann in einem regelmäßigen .ts implementieren Sie es Datei, die jedoch als Nebeneffekt importiert wurde, tatsächlich:

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

Und schließlich verwenden Sie es an anderer Stelle in der Codebasis, entweder mit:

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

myGlobalFunction("Kevin");
Benoit B.
quelle
Danke, dies ist das beste Beispiel, das ich gefunden habe und das gut funktioniert.
Nick G.
6

Eine benutzerdefinierte Schnittstelle erstellen erweitert das Fenster und fügt Ihre benutzerdefinierte Eigenschaft optional hinzu.

Lassen Sie dann das customWindow, das die benutzerdefinierte Oberfläche verwendet, aber mit dem ursprünglichen Fenster bewertet.

Es hat mit dem [email protected] gearbeitet.

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 
Shuizhongyuemin
quelle
5

Für diejenigen, die eine berechnete oder dynamische Eigenschaft für das windowObjekt festlegen möchten, ist dies mit der declare globalMethode nicht möglich . Zur Verdeutlichung für diesen Anwendungsfall

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

Sie könnten versuchen, so etwas zu tun

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

Das obige wird jedoch fehlerhaft sein. Dies liegt daran, dass in Typescript Schnittstellen mit berechneten Eigenschaften nicht gut funktionieren und einen Fehler wie auslösen

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

Um dies zu umgehen, können Sie dem Vorschlag des Castings folgen window, <any>damit Sie dies tun können

(window as any)[DynamicObject.key]
aug
quelle
3
Dies ist so 2019.
Qwerty
4

Mit create-react-app v3.3 fand ich den einfachsten Weg, dies zu erreichen, den WindowTyp in der automatisch generierten zu erweitern react-app-env.d.ts:

interface Window {
    MyNamespace: any;
}
Kris Dover
quelle
3

Zuerst müssen Sie das Fensterobjekt im aktuellen Bereich deklarieren.
Weil Typoskript den Typ des Objekts wissen möchte.
Da das Fensterobjekt an einer anderen Stelle definiert ist, können Sie es nicht neu definieren.
Sie können dies jedoch wie folgt deklarieren:

declare var window: any;

Dadurch wird das Fensterobjekt nicht neu definiert oder es wird keine weitere Variable mit Namen erstellt window.
Dies bedeutet, dass das Fenster an einer anderen Stelle definiert ist und Sie es nur im aktuellen Bereich referenzieren.

Dann können Sie einfach auf Ihr MyNamespace-Objekt verweisen, indem Sie:

window.MyNamespace

Oder Sie können die neue Eigenschaft für das windowObjekt einfach festlegen, indem Sie:

window.MyNamespace = MyObject

Und jetzt wird sich das Typoskript nicht beschweren.

Mav55
quelle
Darf ich Ihren Anwendungsfall kennen, wenn Sie wissen möchten, wo er windowdefiniert ist?
Mav55
2

Ich wollte dies heute in einer Angular (6) -Bibliothek verwenden, und es dauerte eine Weile, bis dies wie erwartet funktioniert.

Damit meine Bibliothek Deklarationen verwenden konnte, musste ich die d.tsErweiterung für die Datei verwenden, die die neuen Eigenschaften des globalen Objekts deklariert.

Am Ende hatte die Datei also Folgendes:

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

Vergessen Sie nach dem Erstellen nicht, es in Ihrem zu belichten public_api.ts.

Das hat es für mich getan. Hoffe das hilft.

Dolch
quelle