Come si imposta esplicitamente una nuova proprietà su `window` in TypeScript?


621

Ho impostato spazi dei nomi globali per i miei oggetti impostando esplicitamente una proprietà su window.

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

TypeScript sottolinea MyNamespacee si lamenta che:

La proprietà 'MyNamespace' non esiste sul valore di tipo 'window' any "

Posso far funzionare il codice dichiarando MyNamespacecome una variabile ambientale e rilasciando l' windowesplicito, ma non voglio farlo.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Come posso rimanere windowlì e rendere felice TypeScript?

Come nota a margine, trovo particolarmente divertente che TypeScript si lamenti dal momento che mi dice che windowè di tipo anyche sicuramente può contenere qualsiasi cosa.


vedi Link
MHS

Risposte:


692

Ho appena trovato la risposta a questa domanda in un'altra risposta StackOverflow .

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

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

Fondamentalmente devi estendere l' windowinterfaccia esistente per parlarne della tua nuova proprietà.


12
Nota la W maiuscola in Window. Mi ha fatto inciampare.
Aj

2
Non sono riuscito a compilare questo con tsc 1.0.1.0. Tuttavia, la risposta di Blake Mitchell ha funzionato per me.
Pat

43
Tieni presente che funziona solo se dichiarato in un .d.tsfile separato . Non funziona se utilizzato in un .tsfile che utilizza le importazioni e le esportazioni stesse. Vedere questa risposta .
cdauth,

36
Le declare global { interface Window { ... } }opere con tipografico 2.5.2, senza bisogno di .d.tsfile come di cui sopra
tanguy_k

2
All'inizio dattiloscritto mi ha detto: error TS1234: An ambient module declaration is only allowed at the top level in a file.quando l'ho messo all'inizio del file, ha risolto quell'errore, quindi potresti doverlo fare anche tu.
Zelphir Kaltstahl,

398

Per mantenerlo dinamico, basta usare:

(<any>window).MyNamespace

9
Qualche idea su come farlo in un file tsx?
velop

2
@martin: <any> lo rende esplicito, quindi funziona bene, credo. Altri: se stai usando Typescript con React o un altro ambiente JSX (con conseguente sintassi TSX) dovrai usare asinvece di <>. Vedi la risposta di @ david-boyd di seguito .
Don

31
Ho dovuto usare (qualsiasi finestra). MyNamespace
Arsalan Ahmad,

Allo stesso modo per this-return (<any> this)['something in scope']
HankCa il

1
Ho dovuto disabilitare tslint su quella linea con/* tslint:disable */
Michael Yagudaev il

197

A PARTIRE DA TYPESCRIPT ^ 3.4.3 QUESTA SOLUZIONE NON FUNZIONA PIÙ

O...

puoi semplicemente digitare:

window['MyNamespace']

e non otterrai un errore di compilazione e funziona come la digitazione window.MyNamespace


15
ma probabilmente otterrai un errore tslint ... Se ne hai uno ovviamente
smnbbrv

69
Questo vola completamente di fronte alla tipizzazione forte, l'idea alla base di TypeScript.
d512,

18
Anche @ user1334007 usando i globali. Tuttavia, è necessario un codice legacy.
nathancahill

3
@Ramesh window['MyNamespace']()(basta aggiungere parentesi)
iBaff

6
"Questo vola completamente di fronte alla tipizzazione forte, l'idea alla base di TypeScript.". BENE!
Cody,

188

Usando TSX? Nessuna delle altre risposte funzionava per me.

Ecco cosa ho fatto:

(window as any).MyNamespace


44
È lo stesso tranne quando si utilizza TSX, perché <any>viene interpretato come JSX, non un cast di tipo.
Jake Boone,

1
Grazie @David, ha funzionato come un incantesimo con la nuova app Crea React e la versione dattiloscritta, precedentemente funzionava: (<nella> finestra) .MyNamespace, ma ora si sta rompendo nella nuova versione dattiloscritta 3.5.x.
Jignesh Raval,

finestra come una? Allora perché dovresti usare il dattiloscritto? Usa Javascript x)
MarcoLe

71

La risposta accettata è quella che utilizzavo, ma con TypeScript 0.9. * Non funziona più. La nuova definizione diWindow dell'interfaccia sembra sostituire completamente la definizione integrata, anziché aumentarla.

Ho preso invece a fare questo:

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

declare var window: MyWindow;

AGGIORNAMENTO: con TypeScript 0.9.5 la risposta accettata funziona di nuovo.


2
Funziona anche con i moduli usati da TypeScript 2.0.8. Esempio: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() Quindi puoi chiamare foo () ad esempio dalla console di Chrome Dev Tools come mc.foo()
Martin Majewski,

Questa è una risposta molto bella se non vuoi dichiarare qualcosa come globale. D'altra parte, è necessario chiamare declare var...tutti i file necessari.
Puce,

Più uno per questo approccio perché in questo caso non sei in conflitto con gli altri pacchetti che estendono la finestra globale nel monorepo.
Dmitrii Sorin,

Grazie! Migliore risposta IMO. Non si dovrebbe ignorare Windowil semplice fatto che non è una finestra alla vaniglia. Anche solo aggirare il controllo del tipo o cercare di ingannare TS non è il modo per farlo.
Rutger Willems,

Purtroppo non funziona a partire da TS 3.6
Jacob Soderlund,

65

I Global sono "cattivi" :), penso che il modo migliore per avere anche la portabilità sia:

Per prima cosa esporti l'interfaccia: (es.: ./Custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Secondo, importa

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

Terza finestra var globale con CustomWindow

declare let window: CustomWindow;

In questo modo non hai anche una linea rossa in IDE diversi se usi con attributi esistenti di oggetto finestra, quindi alla fine prova:

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

Testato con Typescript 2.4.xe più recente!


1
potresti voler espandere il motivo per cui i globi sono cattivi. Nel nostro caso stiamo usando un'API non standardizzata sull'oggetto finestra. È polifillato per ora. È davvero malvagio?
Mathijs Segers,

1
@MathijsSegers non conosco in modo particolare il tuo caso, ma ... i globi sono cattivi non è solo javascript .. è in tutte le lingue .. alcuni motivi sono: - Non puoi applicare bene il modello di progettazione - Problema di memoria e prestazioni (Tu averli ovunque, prova a collegare qualcosa all'oggetto String e vedi cosa succede quando esegui una nuova stringa) - Nomina la collisione causando effetti collaterali sul codice .. (specialmente su javascript che ha una natura asincrona) Posso continuare ma penso che puoi immaginare resto ...
onalbi

1
@onabi Sto letteralmente parlando di una funzionalità del browser. È disponibile a livello globale dal momento che è il browser. Suggeriresti davvero che è ancora sbagliato cercare definizioni globali? Voglio dire che potremmo implementare Ponyfills, ma questo farebbe gonfiare i browser che effettivamente supportano la funzione (ad es. API a schermo intero). Detto questo, non credo che tutti i globi siano perse malvagie.
Mathijs Segers,

2
@MathijsSegers secondo me la variabile globale dovrebbe essere evitata per essere utilizzata o modificata dove possiamo .. è vero che la finestra è disponibile dal momento che esiste il browser ma è anche vero che era in mutazione tutto il tempo .. quindi se per esempio definiamo ora window.feature = 'Feature'; e questo viene usato in modo massiccio sul codice .. cosa succede se la finestra viene aggiunta dai browser su tutto il codice questa funzione viene ignorata .. comunque ho dato una spiegazione della mia frase non è andare contro di te .. saluti ...
onalbi,


43

Per coloro che usano la CLI angolare è semplice:

src / polyfills.ts

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

my-custom-utils.ts

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

Se stai usando IntelliJ, devi anche modificare le seguenti impostazioni nell'IDE prima che i tuoi nuovi polyfill prendano:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.

1
declare globalè il trucco, e questa risposta non è proprio specifica per la CLI angolare ...
Avindra Goolcharan,

31

Non ho bisogno di farlo molto spesso, l'unico caso che ho avuto è stato quando ho usato Redux Devtools con il middleware.

Ho semplicemente fatto:

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

Oppure potresti fare:

let myWindow = window as any;

e poi myWindow.myProp = 'my value';


1
In questo caso puoi anche installare il pacchetto npm, che contiene tutte le definizioni - come specificato nei documenti
bbrinx

Puoi farlo, ma non è la risposta alla domanda, ma piuttosto una soluzione alternativa
Vitalij

Grazie a questo, ho potuto abilitare correttamente gli strumenti di redux in dattiloscritto usando (window as any) .__ REDUX_DEVTOOLS_EXTENSION__ && (window as any) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario

20

La maggior parte delle altre risposte non sono perfette.

  • Alcuni di loro semplicemente sopprimono l'inferenza di tipo per il negozio.
  • Alcuni altri si preoccupano solo della variabile globale come spazio dei nomi, ma non come interfaccia / classe

Ho anche riscontrato il problema simile questa mattina. Ho provato così tante "soluzioni" su SO, ma nessuna di esse non produce assolutamente alcun errore di tipo e abilita l'attivazione del salto di tipo in IDE (webstorm o vscode).

Finalmente da qui

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

, Trovo una soluzione ragionevole per collegare le tipizzazioni per la variabile globale che funge sia da interfaccia / classe sia da spazio dei nomi .

L'esempio è sotto:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Ora, il codice sopra unisce le tipizzazioni dello spazio dei nomi MyNamespacee dell'interfaccia MyNamespacenella variabile globale myNamespace(la proprietà della finestra).


2
Grazie - questo è l'unico che puoi apparentemente usare in un contesto non modulo ambientale.
Tyler Sebastian,


13

Ecco come farlo, se stai usando TypeScript Definition Manager !

npm install typings --global

Creare typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Installa la tua digitazione personalizzata:

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

Fatto, usalo‌ ! Typescript non si lamenterà più:

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

1
FWIW è typingsstato reso obsoleto con Typescript 2.0 (metà 2016) ed è stato archiviato dal proprietario.
sig

13

Typscript non esegue il typecheck sulle proprietà della stringa.

window["newProperty"] = customObj;

Idealmente, lo scenario variabile globale dovrebbe essere evitato. Lo uso a volte per eseguire il debug di un oggetto nella console del browser.



8

Se stai usando Typescript 3.x, potresti essere in grado di omettere la declare globalparte nelle altre risposte e invece usare semplicemente:

interface Window {
  someValue: string
  another: boolean
}

Questo ha funzionato con me durante l'utilizzo di Typescript 3.3, WebPack e TSLint.


Non funziona ^3.4.3. Questa risposta ha funzionato per me stackoverflow.com/a/42841166/1114926
Verde

7

Per riferimento (questa è la risposta corretta):

All'interno di un .d.tsfile di definizione

type MyGlobalFunctionType = (name: string) => void

Se lavori nel browser, aggiungi membri al contesto della finestra del browser riaprendo l'interfaccia di Windows:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Stessa idea per NodeJS:

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

Ora dichiari la variabile radice (che in realtà vivrà su finestra o globale)

declare const myGlobalFunction: MyGlobalFunctionType;

Quindi in un .tsfile normale , ma importato come effetto collaterale, lo si implementa effettivamente:

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

E infine usalo altrove nel codebase, con uno dei due:

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

myGlobalFunction("Kevin");

Grazie, questo è il miglior esempio che ho trovato e funziona bene.
Nick G.

6

Crea un'interfaccia personalizzata estende la finestra e aggiungi la tua proprietà personalizzata come facoltativa.

Quindi, lascia che la CustomWindow utilizzi l'interfaccia personalizzata, ma che sia valutata con la finestra originale.

Ha funzionato con il typescript@3.1.3.

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 

5

Per coloro che desiderano impostare una proprietà calcolata o dinamica windowsull'oggetto, questo non sarà possibile con il declare globalmetodo. Per chiarire questo caso d'uso

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

Potresti provare a fare qualcosa del genere

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

Quanto sopra sarà comunque un errore. Questo perché in Typescript, le interfacce non giocano bene con le proprietà calcolate e generano un errore simile

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

Per ovviare a questo, puoi andare con il suggerimento di lanciare windowin <any>modo da poterlo fare

(window as any)[DynamicObject.key]

3
Questo è il 2019.
Qwerty,

4

Usando create -eagire-app v3.3 ho trovato il modo più semplice per raggiungere questo obiettivo era estendere il Windowtipo in auto-generato react-app-env.d.ts:

interface Window {
    MyNamespace: any;
}

3

Innanzitutto è necessario dichiarare l'oggetto finestra nell'ambito corrente.
Perché il dattiloscritto vorrebbe conoscere il tipo di oggetto.
Poiché l'oggetto finestra è definito altrove, non è possibile ridefinirlo.
Ma puoi dichiararlo come segue: -

declare var window: any;

Ciò non ridefinirà l'oggetto finestra o non creerà un'altra variabile con nome window.
Ciò significa che la finestra è definita da qualche altra parte e la si sta semplicemente facendo riferimento nell'ambito corrente.

Quindi puoi fare riferimento al tuo oggetto MyNamespace semplicemente: -

window.MyNamespace

Oppure puoi impostare la nuova proprietà windowsull'oggetto semplicemente: -

window.MyNamespace = MyObject

E ora il dattiloscritto non si lamenterà.


posso sapere il tuo caso d'uso di voler sapere dove windowè definito?
Mav55,

2

Volevo usarlo oggi in una libreria Angular (6) e mi ci è voluto un po 'per farlo funzionare come previsto.

Affinché la mia libreria potesse usare le dichiarazioni, ho dovuto usare l' d.tsestensione per il file che dichiara le nuove proprietà dell'oggetto globale.

Quindi alla fine, il file è finito con qualcosa del tipo:

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

Una volta creato, non dimenticare di esporlo nel tuo public_api.ts.

Che ha fatto per me. Spero che sia di aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.