Einleitung: Warum das Hinzufügen einer Eigenschaft zu window für jeden TypeScript-Entwickler ein Kopfschmerz ist?
Stellen Sie sich folgende Situation vor: Sie schreiben eine moderne SPA-Anwendung in TypeScript. Alles läuft reibungslos, bis die Notwendigkeit entsteht, einen globalen Zustand oder eine Integration mit einem externen Skript (z. B. Analytik, Karten oder Zahlungs-Gateway) zu speichern. Sie schreiben:
window.myAppConfig = { apiKey: '123' };Und dann zeigt TypeScript Ihnen einen roten Fehler an: "Property 'myAppConfig' does not exist on type 'Window & typeof globalThis'". Kommt Ihnen bekannt vor? Diese Frage hat Tausende von Aufrufen auf Stack Overflow gesammelt, und heute geben wir nicht nur eine fertige Lösung, sondern schauen auch unter die Haube von TypeScript, um zu verstehen, warum das passiert und wie man elegant und sicher damit umgeht.
In diesem Artikel werden wir mehrere Ansätze durchgehen – von "Krücken" für Anfänger bis hin zu professionellen Mustern, die Senior-Entwickler verwenden. Los geht's!
Unter der Haube: Warum meckert TypeScript bei window.myProp?
Um das Problem zu verstehen, muss man sich daran erinnern, dass TypeScript eine Erweiterung von JavaScript ist, die statische Typisierung hinzufügt. Das window-Objekt im Browser ist ein globales Objekt, das einen vordefinierten Typ (Window) hat. Dieser Typ ist in den Standardbibliotheken von TypeScript (lib.dom.d.ts) beschrieben und enthält alle Standardeigenschaften: document, location, setTimeout und Hunderte andere.
Wenn Sie versuchen, window.myProp zuzuweisen, prüft TypeScript: "Gibt es diese Eigenschaft im Interface Window?" Nein – also ein Fehler. Dies ist ein Schutz vor Tippfehlern und versehentlicher Verschmutzung des globalen Gültigkeitsbereichs. Aber was tun, wenn Sie bewusst window erweitern möchten?
Methode 1: Die einfachste, aber gefährliche – as any
Die erste Option, die einem Anfänger in den Sinn kommt, ist, TypeScript einfach mit einer Typumwandlung "zum Schweigen zu bringen":
(window as any).myAppConfig = { apiKey: '123' };Vorteile: schnell, funktioniert, erfordert keine zusätzlichen Definitionen.
Nachteile: Sie verlieren alle Vorteile von TypeScript. Keine Autovervollständigung, keine Typprüfung. Wenn Sie versehentlich myAppConfigg schreiben, gibt es keinen Fehler, aber die Anwendung wird zur Laufzeit abstürzen. Das ist, als würde man die Airbags im Auto ausschalten – man kann fahren, aber es ist riskant.
Wann verwenden: Nur für schnelles Prototyping oder wenn Sie sicher sind, dass die Eigenschaft nur einmal verwendet und dann gelöscht wird.
Methode 2: Erweiterung des globalen Interfaces (Interface Merging)
TypeScript unterstützt Interface Merging (Deklarations-Merging). Dies ist ein Schlüsselmechanismus, der es ermöglicht, dem bereits vorhandenen Typ Window neue Eigenschaften "hinzuzufügen". Dazu müssen Sie ein Interface mit demselben Namen im globalen Gültigkeitsbereich deklarieren.
Erstellen Sie eine Datei global.d.ts (oder fügen Sie sie zu einer beliebigen .d.ts-Datei Ihres Projekts hinzu):
// global.d.tsinterface Window { myAppConfig: { apiKey: string; debugMode?: boolean; }; myAnalyticsFunction: (event: string, data: object) => void;}Jetzt können Sie schreiben:
window.myAppConfig = { apiKey: '123' };window.myAnalyticsFunction('page_view', { page: '/home' });
// TypeScript wird vorschlagen, dass apiKey ein string istconsole.log(window.myAppConfig.apiKey); // ✅ Ohne FehlerWie funktioniert das unter der Haube? TypeScript führt automatisch ein Merging aller Interface-Deklarationen mit demselben Namen durch. Wenn es in lib.dom.d.ts bereits ein interface Window { ... } gibt, fügt Ihr Interface mit demselben Namen diesem einfach neue Eigenschaften hinzu. Dies wird als glob bezeichnet.