Jak jawnie ustawić nową właściwość na `window` w TypeScript?

619

Konfiguruję globalne przestrzenie nazw dla moich obiektów, ustawiając jawnie właściwość na window.

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

TypeScript podkreśla MyNamespacei narzeka, że:

Właściwość „MyNamespace” nie istnieje dla wartości typu „window” any ”

Mogę sprawić, aby kod działał, deklarując MyNamespacejako zmienną otoczenia i usuwając windowjawność, ale nie chcę tego robić.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Jak mogę windowtam pozostać i sprawić, że TypeScript będzie szczęśliwy?

Na marginesie uważam za szczególnie zabawne, że narzeka TypeScript, ponieważ mówi mi, że windowjest to typ, anyktóry zdecydowanie może zawierać wszystko.

joshuapoehls
źródło
patrz Link
MHS

Odpowiedzi:

690

Właśnie znalazłem odpowiedź na to pytanie w odpowiedzi na inne pytanie StackOverflow .

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

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

Zasadniczo musisz rozszerzyć istniejący windowinterfejs, aby poinformować go o swojej nowej właściwości.

joshuapoehls
źródło
11
Zwróć uwagę na wielką literę W w oknie. To mnie potknęło.
ajm
2
Nie mogłem tego skompilować z tsc 1.0.1.0. Jednak odpowiedź Blake'a Mitchella zadziałała dla mnie.
Pat
43
Pamiętaj, że działa to tylko wtedy, gdy zostanie zadeklarowane w osobnym .d.tspliku. Nie działa, gdy jest używany w .tspliku, który używa samego importu i eksportu. Zobacz tę odpowiedź .
cdauth
36
Te declare global { interface Window { ... } }prace z maszynopis 2.5.2, nie ma potrzeby do .d.tspliku, jak wspomniano powyżej
tanguy_k
2
Na początku maszynopis powiedział mi: error TS1234: An ambient module declaration is only allowed at the top level in a file.kiedy umieściłem go na górze pliku, naprawił ten błąd, więc być może będziesz musiał to zrobić.
Zelphir Kaltstahl
397

Aby zachować dynamikę, po prostu użyj:

(<any>window).MyNamespace
chinupson
źródło
9
Wszelkie pomysły, jak to zrobić w pliku tsx?
velop
2
@martin: <any> wyraźnie mówi, więc wierzę, że działa dobrze. Inne: Jeśli używasz Typescript z React lub innym środowiskiem JSX (skutkującym składnią TSX), będziesz musiał użyć aszamiast <>. Zobacz odpowiedź @ david-boyd poniżej .
Don
31
Musiałem użyć (okno jak każde). MyNamespace
Arsalan Ahmad
Podobnie dla this-return (<any> this)['something in scope']
HankCa
1
Musiałem wyłączyć tslint na tej linii z/* tslint:disable */
Michael Yagudaev
197

STANU TYPU ^ 3.4.3 TO ROZWIĄZANIE NIE DZIAŁA DŁUGO

Lub...

możesz po prostu wpisać:

window['MyNamespace']

i nie pojawi się błąd kompilacji i działa tak samo jak pisanie window.MyNamespace

Evan Larsen
źródło
15
ale prawdopodobnie wystąpi błąd tslint ... Jeśli masz jeden
smnbbrv
69
To całkowicie leci w obliczu silnego pisania, cała idea stojąca za TypeScript.
d512
18
@ user1334007 używa również globals. Jednak niektóre starsze kody tego wymagają.
nathancahill
3
@Ramesh window['MyNamespace']()(wystarczy dodać nawiasy
kwadratowe
6
„To całkowicie leci w obliczu silnego pisania, cała idea stojąca za TypeScript.”. DOBRZE!
Cody
188

Używasz TSX? Żadna z pozostałych odpowiedzi nie działała dla mnie.

Oto co zrobiłem:

(window as any).MyNamespace
David Boyd
źródło
4
Tak jak w (<any> window).MyNamespacerzeczywistości
Dmitrij Parzhitsky
44
Jest tak samo, z wyjątkiem używania TSX, ponieważ <any>interpretowany jest jako JSX, a nie rzut typu.
Jake Boone
1
Dzięki @David, działało to jak urok nowej aplikacji Create React i wersji maszynopisu. Wcześniej to działało: (<każde okno). MyNamespace, ale teraz psuje się w nowej wersji maszynopisu 3.5.x.
Jignesh Raval
jak okno? Dlaczego więc miałbyś używać maszynopisu? Wystarczy użyć Javascript x)
MarcoLe
71

Zaakceptowałem odpowiedź, z której korzystałem, ale z TypeScript 0.9. * To już nie działa. Nowa definicja Windowinterfejsu wydaje się całkowicie zastępować wbudowaną definicję, zamiast ją rozszerzać.

Zamiast tego zdecydowałem się to zrobić:

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

declare var window: MyWindow;

AKTUALIZACJA: Z TypeScript 0.9.5 zaakceptowana odpowiedź znów działa.

Blake Mitchell
źródło
2
Działa to również z modułami używanymi przez TypeScript 2.0.8. Przykład: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() wtedy możesz wywołać foo () np. Z konsoli Chrome Dev Tools, np. mc.foo()
Martin Majewski
To bardzo miła odpowiedź, jeśli nie chcesz deklarować czegoś jako globalnego. Z drugiej strony musisz wywołać declare var...każdy potrzebny plik.
Puce
Plus jeden za to podejście, ponieważ w tym przypadku nie kolidujesz z innymi pakietami, które rozszerzają globalne okno w monorepo.
Dmitrii Sorin
Dziękuję Ci! Najlepsza odpowiedź IMO. Nie należy przesłonić Windowprostego faktu, że nie jest to okno waniliowe. Samo obejście sprawdzania typu lub próba oszukiwania TS również nie jest na to sposobem.
Rutger Willems,
Niestety nie działa to od wersji TS 3.6
Jacob Soderlund,
65

Globalne są „złe” :), myślę, że najlepszym sposobem na przeniesienie jest:

Najpierw eksportujesz interfejs: (np .: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Drugi import

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

Trzecie rzutowanie globalnego okna var z CustomWindow

declare let window: CustomWindow;

W ten sposób nie masz czerwonej linii w innym IDE, jeśli używasz z istniejącymi atrybutami obiektu okna, więc na koniec spróbuj:

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

Testowane z Typescript 2.4.x i nowszymi!

onalbi
źródło
1
możesz chcieć wyjaśnić, dlaczego globały są złe. W naszym przypadku używamy niestandardowego interfejsu API na obiekcie okna. Na razie jest wypełniony. Czy to naprawdę złe?
Mathijs Segers,
1
@MathijsSegers Nie znam szczególnie twojej sprawy, ale .. o globale są złe, to nie tylko o javascript .. jest w każdym języku .. z jakiegoś powodu: - Nie możesz dobrze zastosować wzorca projektowego - Problem z pamięcią i wydajnością (You mieć je wszędzie, spróbuj dołączyć coś do String Object i zobaczyć, co się stanie, gdy zrobisz nowy String) - Kolizja nazw powodująca efekt uboczny w kodzie .. (szczególnie w javascript, które mają charakter asynchroniczny) Mogę kontynuować, ale myślę, że możesz sobie wyobrazić reszta ...
onalbi,
1
@onabi Mówię dosłownie o funkcji przeglądarki. Jest dostępny na całym świecie, ponieważ jest to przeglądarka. Naprawdę zasugerowałbyś, że wciąż źle jest szukać globalnych definicji? Mam na myśli, że moglibyśmy wdrożyć Ponyfills, ale spowodowałoby to wzdęcie przeglądarek, które faktycznie obsługują tę funkcję (np. Pełnoekranowy interfejs API). To powiedziawszy, nie wierzę, że wszystkie globały są złem.
Mathijs Segers,
2
@MathijsSegers, moim zdaniem, należy unikać używania lub modyfikowania zmiennej globalnej tam, gdzie to możliwe. Prawdą jest, że okno jest dostępne, ponieważ istnieje przeglądarka, ale prawdą jest również to, że mutacja była przez cały czas .. więc jeśli na przykład zdefiniujemy teraz window.feature = 'Feature'; i jest to używane w masowy sposób na kodzie .. co się stanie, jeśli przeglądarka doda cały kod. funkcja jest dodawana do wszystkich kodów, ta funkcja jest nadpisywana .. w każdym razie podałem wyjaśnienie mojego zdania, że ​​nie jest przeciwko tobie ... Pozdrawiam ...
onalbi
43

Dla osób korzystających z Angular CLI jest to proste:

src / polyfills.ts

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

my-custom-utils.ts

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

Jeśli używasz IntelliJ, musisz także zmienić następujące ustawienie w IDE przed pobraniem nowych wielopełniaczy:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.
Stephen Paul
źródło
1
declare globaljest sztuczką, a ta odpowiedź nie jest specjalnie specyficzna dla Angular CLI ...
Avindra Goolcharan
31

Nie muszę tego robić zbyt często, jedynym przypadkiem było użycie Redux Devtools z oprogramowaniem pośrednim.

Po prostu zrobiłem:

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

Lub możesz zrobić:

let myWindow = window as any;

i wtedy myWindow.myProp = 'my value';

Dimitar Nikowski
źródło
1
W takim przypadku możesz również zainstalować pakiet npm, który zawiera wszystkie definicje - określone w dokumentacji
bbrinx 30.01.2019
Możesz to zrobić, ale to nie jest odpowiedź na pytanie, ale raczej obejście
Vitalij
Dzięki temu mogłem poprawnie włączyć narzędzia redux pod pismem maszynowym, używając (Window as any) .__ REDUX_DEVTOOLS_EXTENSION__ && (window as any) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario
20

Większość innych odpowiedzi nie jest idealna.

  • Niektóre z nich po prostu tłumią wnioskowanie o typie dla sklepu.
  • Niektóre inne dbają tylko o zmienną globalną jako przestrzeń nazw, ale nie jako interfejs / klasę

Podobny problem napotykam także dziś rano. Próbowałem tak wielu „rozwiązań” na SO, ale żadne z nich nie generuje absolutnie żadnego błędu typu i włącza wyzwalanie skoków typów w IDE (burza internetowa lub vscode).

Wreszcie stąd

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

, Znajduję rozsądne rozwiązanie, aby dołączyć typy dla zmiennej globalnej, która działa zarówno jako interfejs / klasa, jak i przestrzeń nazw .

Przykład jest poniżej:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Teraz powyższy kod łączy typy przestrzeni nazw MyNamespacei interfejsu MyNamespacew zmienną globalną myNamespace(właściwość okna).

e-chmura
źródło
2
Dzięki - jest to jedyny taki, który najwyraźniej możesz używać w otaczającym kontekście niemodułowym.
Tyler Sebastian,
13

Oto jak to zrobić, jeśli używasz Menedżera definicji TypeScript !

npm install typings --global

Utwórz typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Zainstaluj niestandardowe pisanie:

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

Gotowe, użyj go‌ ! Maszynopis nie będzie już narzekał:

window.MyNamespace = window.MyNamespace || {};
Nik Sumeiko
źródło
1
FWIW typingsstało się przestarzałe z Typescript 2.0 (połowa 2016 r.) I zostało zarchiwizowane przez właściciela.
mrm,
13

Maszynopis nie wykonuje kontroli typu we właściwościach łańcucha.

window["newProperty"] = customObj;

Najlepiej byłoby uniknąć scenariusza zmiennej globalnej. Czasami używam go do debugowania obiektu w konsoli przeglądarki.

Irshad
źródło
8

Jeśli używasz Typescript 3.x, możesz pominąć declare globalczęść w innych odpowiedziach, a zamiast tego użyć:

interface Window {
  someValue: string
  another: boolean
}

Działa to ze mną podczas korzystania z Typescript 3.3, WebPack i TSLint.

Dana Woodman
źródło
Nie działa w ^3.4.3. Ta odpowiedź działała na mnie stackoverflow.com/a/42841166/1114926
Zielony
7

Dla odniesienia (to jest poprawna odpowiedź):

Wewnątrz .d.tspliku definicji

type MyGlobalFunctionType = (name: string) => void

Jeśli pracujesz w przeglądarce, dodajesz członków do kontekstu okna przeglądarki, ponownie otwierając interfejs Windows:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Ten sam pomysł dla NodeJS:

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

Teraz zadeklarujesz zmienną główną (która faktycznie będzie działać w oknie lub globalnie)

declare const myGlobalFunction: MyGlobalFunctionType;

Następnie w zwykłym .tspliku, ale zaimportowanym jako efekt uboczny, faktycznie go implementujesz:

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

I w końcu użyj go gdzie indziej w bazie kodu, albo:

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

myGlobalFunction("Kevin");
Benoit B.
źródło
Dzięki, to najlepszy przykład, jaki znalazłem i działa dobrze.
Nick G.
6

Utwórz niestandardowy interfejs rozszerza okno i dodaj niestandardową właściwość jako opcjonalną.

Następnie pozwól customWindow, które korzystają z niestandardowego interfejsu, ale wyceniane w oryginalnym oknie.

Działa z [email protected].

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 
shuizhongyuemin
źródło
5

Dla tych, którzy chcą ustawić obliczoną lub dynamiczną właściwość windowobiektu, przekonasz się, że ta declare globalmetoda nie jest możliwa . Wyjaśnienie dla tego przypadku użycia

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

Możesz spróbować zrobić coś takiego

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

Powyższe spowoduje błąd. Wynika to z faktu, że w Typescript interfejsy nie działają dobrze z obliczonymi właściwościami i generują błąd podobny do tego

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

Aby obejść ten problem, można przejść z sugerować odlewania windowdo <any>tak można zrobić

(window as any)[DynamicObject.key]
sierpień
źródło
3
Tak jest w 2019 r.
Qwerty
4

Za pomocą create-React-app v3.3 znalazłem najłatwiejszy sposób na osiągnięcie tego celu, aby rozszerzyć Windowtyp w generowanym automatycznie react-app-env.d.ts:

interface Window {
    MyNamespace: any;
}
Kris Dover
źródło
3

Najpierw musisz zadeklarować obiekt okna w bieżącym zakresie.
Ponieważ maszynopis chciałby poznać typ obiektu.
Ponieważ obiekt okna jest zdefiniowany gdzie indziej, nie można go przedefiniować.
Ale możesz to zadeklarować w następujący sposób:

declare var window: any;

To nie przedefiniuje obiektu okna lub nie utworzy innej zmiennej o nazwie window.
Oznacza to, że okno jest zdefiniowane gdzie indziej i po prostu odwołujesz się do niego w bieżącym zakresie.

Następnie możesz odwołać się do obiektu MyNamespace po prostu przez: -

window.MyNamespace

Lub możesz ustawić nową właściwość na windowobiekcie po prostu przez: -

window.MyNamespace = MyObject

A teraz maszynopis nie będzie narzekał.

Mav55
źródło
czy mogę poznać twój przypadek użycia, jeśli chcesz wiedzieć, gdzie windowjest zdefiniowany?
Mav55
2

Chciałem użyć tego dzisiaj w bibliotece Angular (6) i zajęło mi to trochę czasu, aby to zadziałało zgodnie z oczekiwaniami.

Aby moja biblioteka mogła korzystać z deklaracji, musiałem użyć d.ts rozszerzenia dla pliku deklarującego nowe właściwości obiektu globalnego.

Ostatecznie plik skończył się tak:

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

Po utworzeniu nie zapomnij go ujawnić w swoim public_api.ts .

To mi zrobiło. Mam nadzieję że to pomoże.

Sztylet
źródło