¿Cómo se establece explícitamente una nueva propiedad en `window` en TypeScript?

621

Configuro espacios de nombres globales para mis objetos estableciendo explícitamente una propiedad en window.

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

TypeScript subraya MyNamespacey se queja de que:

La propiedad 'MyNamespace' no existe en el valor de tipo 'window' any "

Puedo hacer que el código funcione declarándolo MyNamespacecomo una variable ambiental y descartando la windowexplicitación, pero no quiero hacer eso.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

¿Cómo puedo mantenerme windowallí y hacer feliz a TypeScript?

Como nota al margen, me parece especialmente divertido que TypeScript se queje, ya que me dice que windowes de tipo anyque definitivamente puede contener cualquier cosa.

joshuapoehls
fuente
ver Enlace
MHS

Respuestas:

693

Acabo de encontrar la respuesta a esto en la respuesta de otra pregunta de StackOverflow .

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

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

Básicamente, necesita extender la windowinterfaz existente para informarle sobre su nueva propiedad.

joshuapoehls
fuente
12
Tenga en cuenta la W mayúscula en la ventana. Eso me hizo tropezar.
ajm
2
No pude hacer esto para compilar con tsc 1.0.1.0. Sin embargo, la respuesta de Blake Mitchell funcionó para mí.
Pat
43
Tenga en cuenta que esto solo funciona cuando se declara en un .d.tsarchivo separado . No funciona cuando se usa en un .tsarchivo que usa importaciones y se exporta a sí mismo. Mira esta respuesta .
cdauth
36
Las declare global { interface Window { ... } }obras con texto mecanografiado 2.5.2, sin necesidad de .d.tsarchivos como se mencionó anteriormente
tanguy_k
2
Al principio, el mecanografiado me dijo: error TS1234: An ambient module declaration is only allowed at the top level in a file.cuando lo puse en la parte superior del archivo, solucionó ese error, por lo que es posible que tenga que hacerlo también.
Zelphir Kaltstahl el
398

Para mantenerlo dinámico, solo use:

(<any>window).MyNamespace
Chinupson
fuente
99
¿Alguna idea de cómo hacer esto en un archivo tsx?
velop
2
@martin: <any> lo hace explícito, por lo que funciona bien, creo. Otros: si está utilizando Typecript con React u otro entorno JSX (lo que da como resultado la sintaxis de TSX), deberá utilizarlo en aslugar de hacerlo <>. Vea la respuesta de @ david-boyd a continuación .
Don
31
Tuve que usar (ventana como cualquiera) .MyNamespace
Arsalan Ahmad
Del mismo modo para this-return (<any> this)['something in scope']
HankCa
1
He tenido que desactivar tslint en esa línea con/* tslint:disable */
Michael Yagudaev
197

A PARTIR DE TYPESCRIPT ^ 3.4.3 ESTA SOLUCIÓN NO FUNCIONA

O...

solo puedes escribir:

window['MyNamespace']

y no obtendrá un error de compilación y funciona igual que escribir window.MyNamespace

Evan Larsen
fuente
15
pero probablemente obtendrá un error tslint ... Si tiene uno, por supuesto
smnbbrv
69
Esto va completamente en contra de la escritura fuerte, toda la idea detrás de TypeScript.
d512
18
@ user1334007 usando globals también lo hace. Sin embargo, algunos códigos heredados lo requieren.
nathancahill
3
@Ramesh window['MyNamespace']()(solo agregue paréntesis)
iBaff
66
"Esto va completamente en contra de la escritura fuerte, toda la idea detrás de TypeScript". ¡BUENO!
Cody
188

Usando TSX? Ninguna de las otras respuestas me funcionó.

Esto es lo que hice:

(window as any).MyNamespace
David Boyd
fuente
44
Igual que en (<any> window).MyNamespacerealidad
Dmitry Parzhitsky
44
Es lo mismo excepto cuando se usa TSX, porque <any>se interpreta como JSX, no como un tipo de conversión.
Jake Boone
1
Gracias @David, esto funcionó de maravilla con la nueva aplicación Create React y la versión mecanografiada. Anteriormente, esto funcionaba: (<any> ventana) .MyNamespace, pero ahora está rompiendo en la nueva versión mecanografiada 3.5.x.
Jignesh Raval
ventana como cualquier? Entonces, ¿por qué usarías mecanografiado? Solo use Javascript x)
MarcoLe
71

La respuesta aceptada es lo que solía usar, pero con TypeScript 0.9. * Ya no funciona. La nueva definición de laWindow interfaz parece reemplazar por completo la definición incorporada, en lugar de aumentarla.

En cambio, he decidido hacer esto:

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

declare var window: MyWindow;

ACTUALIZACIÓN: Con TypeScript 0.9.5 la respuesta aceptada está funcionando nuevamente.

Blake Mitchell
fuente
2
Esto también funciona con los módulos que utiliza TypeScript 2.0.8. Ejemplo: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() Entonces puede llamar a foo (), por ejemplo, desde la consola de Chrome Dev Tools como mc.foo()
Martin Majewski,
Esta es una respuesta muy buena si no quieres declarar algo global. Por otro lado, debe llamar declare var...a todos los archivos que necesita.
Puce
Más uno para este enfoque porque en este caso no entra en conflicto con los otros paquetes que extienden la ventana global en el monorepo.
Dmitrii Sorin
¡Gracias! La mejor respuesta de la OMI. Uno no debería anular Windowpor el simple hecho de que no es una ventana de vainilla. Eludir la verificación de tipos o tratar de engañar a TS tampoco es la forma de hacerlo.
Rutger Willems
Esto no funciona a partir de TS 3.6, desafortunadamente
Jacob Soderlund el
65

Global son "malvados" :), creo que la mejor manera de tener también la portabilidad es:

Primero exporta la interfaz: (por ejemplo: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

En segundo lugar importas

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

Tercera ventana de conversión global de conversión con CustomWindow

declare let window: CustomWindow;

De esta manera, no tiene también una línea roja en IDE diferente si lo usa con atributos existentes de objetos de ventana, así que al final intente:

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

Probado con Typecript 2.4.xy más nuevo!

onalbi
fuente
1
es posible que desee ampliar por qué los globales son malvados. En nuestro caso, estamos utilizando una API no estandarizada en el objeto de la ventana. Es polyfilled por ahora. ¿Es eso verdaderamente malvado?
Mathijs Segers
1
@MathijsSegers, no conozco especialmente su caso, pero ... acerca de los globales son malvados no solo se trata de javascript ... está en todos los idiomas ... algunas razones son: - No puede aplicar un patrón de diseño adecuado - Problema de memoria y rendimiento (Usted téngalos en todas partes, intente adjuntar algo al objeto de cadena y vea qué sucede cuando hace una nueva cadena): colisión de nombres que causa efectos secundarios en el código ... (especialmente en javascript que tienen una naturaleza asíncrona) Puedo continuar pero creo que puede crear una imagen del descanso ...
onalbi
1
@onabi Estoy literalmente hablando de una característica del navegador. Está disponible globalmente ya que es el navegador. ¿Realmente sugerirías que todavía está mal buscar definiciones globales? Quiero decir que podríamos implementar Ponyfills, pero esto aumentaría los navegadores que realmente admiten la función (por ejemplo, la API de pantalla completa). Dicho esto, no creo que todos los globales sean malvados perse.
Mathijs Segers
2
@MathijsSegers, en mi opinión, la variable global debe evitarse para usarse o modificarse donde podamos ... es cierto que la ventana está disponible ya que el navegador existe, pero también es cierto que estuvo en mutación todo el tiempo ... así que si por ejemplo definimos ahora window.feature = 'Característica'; y esto se usa de forma masiva en el código ... ¿qué sucede si window.feature es agregado por los navegadores en todo el código? esta función se anula ... de todos modos, di una explicación de mi oración es no ir en contra de usted ... saludos ...
onalbi
43

Para aquellos que usan la CLI Angular es sencillo:

src / polyfills.ts

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

my-custom-utils.ts

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

Si está utilizando IntelliJ, también debe cambiar la siguiente configuración en el IDE antes de que se recuperen sus nuevos polyfills:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.
Stephen Paul
fuente
1
declare globales el truco, y esta respuesta no es realmente específica para Angular CLI ...
Avindra Goolcharan
31

No necesito hacer esto muy a menudo, el único caso que tuve fue cuando utilicé Redux Devtools con middleware.

Simplemente hice:

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

O podrías hacer:

let myWindow = window as any;

y entonces myWindow.myProp = 'my value';

Dimitar Nikovski
fuente
1
En este caso, también puede instalar el paquete npm, que contiene todas las definiciones, como se especifica en los documentos
bbrinx
Puede hacer esto, pero esa no es la respuesta a la pregunta, sino más bien una solución
Vitalij
Gracias a esto, pude habilitar las herramientas redux correctamente bajo el mecanografiado usando (ventana como cualquiera) .__ REDUX_DEVTOOLS_EXTENSION__ && (ventana como cualquiera) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario
20

La mayoría de las otras respuestas no son perfectas.

  • Algunos de ellos simplemente suprimen la inferencia de tipos para la tienda.
  • Algunos de los otros solo se preocupan por la variable global como espacio de nombres, pero no como interfaz / clase

También encuentro el problema similar esta mañana. Probé tantas "soluciones" en SO, pero ninguna de ellas produce absolutamente ningún error de tipo y permite activar el salto de tipo en IDE (tormenta web o vscode).

Finalmente desde aquí

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

, Encuentro una solución razonable para adjuntar tipings para la variable global que actúa como interfaz / clase y espacio de nombres .

El ejemplo está abajo:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Ahora, el código anterior combina las tipificaciones del espacio de nombres MyNamespacey la interfaz MyNamespaceen la variable global myNamespace(la propiedad de la ventana).

e-cloud
fuente
2
Gracias, este es el único que aparentemente puede usar en un contexto ambiental sin módulo.
Tyler Sebastian
13

¡Aquí le mostramos cómo hacerlo si usa el Administrador de definiciones de TypeScript !

npm install typings --global

Crear typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Instala tu mecanografía personalizada:

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

Listo, úsalo ! La mecanografía ya no se quejará:

window.MyNamespace = window.MyNamespace || {};
Nik Sumeiko
fuente
1
FWIW typingsquedó obsoleto con Typecript 2.0 (mediados de 2016) y ha sido archivado por el propietario.
mrm
13

Typscript no realiza la verificación de tipo en las propiedades de cadena.

window["newProperty"] = customObj;

Idealmente, se debe evitar el escenario variable global. Lo uso a veces para depurar un objeto en la consola del navegador.

Irshad
fuente
8

Si está utilizando Typecript 3.x, es posible que pueda omitir la declare globalparte en las otras respuestas y, en su lugar, simplemente use:

interface Window {
  someValue: string
  another: boolean
}

Esto funcionó conmigo cuando usé Typecript 3.3, WebPack y TSLint.

Dana Woodman
fuente
No funciona en ^3.4.3. Esta respuesta funcionó para mí stackoverflow.com/a/42841166/1114926
Verde
7

Como referencia (esta es la respuesta correcta):

Dentro de un .d.tsarchivo de definición

type MyGlobalFunctionType = (name: string) => void

Si trabaja en el navegador, agrega miembros al contexto de la ventana del navegador al volver a abrir la interfaz de Windows:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

La misma idea para NodeJS:

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

Ahora declara la variable raíz (que realmente vivirá en la ventana o global)

declare const myGlobalFunction: MyGlobalFunctionType;

Luego, en un .tsarchivo normal , pero importado como efecto secundario, en realidad lo implementa:

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

Y, finalmente, úselo en otra parte de la base de código, ya sea con:

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

myGlobalFunction("Kevin");
Benoit B.
fuente
Gracias, este es el mejor ejemplo que encontré, y funciona bien.
Nick G.
6

Hacer una interfaz personalizada extiende la Ventana y agrega tu propiedad personalizada como opcional.

Luego, deje la ventana personalizada que usa la interfaz personalizada, pero que se valora con la ventana original.

Se trabajó con el [email protected].

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

customWindow.MyNamespace = customWindow.MyNamespace {} 
shuizhongyuemin
fuente
5

Para aquellos que quieran establecer una propiedad calculada o dinámica en el windowobjeto, encontrarán que eso no es posible con el declare globalmétodo. Para aclarar para este caso de uso

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

Podrías intentar hacer algo como esto

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

Sin embargo, lo anterior será un error. Esto se debe a que, en Typecript, las interfaces no funcionan bien con las propiedades calculadas y arrojan un error como

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

Para evitar esto, puedes ir con la sugerencia de emitir windowpara <any>que puedas hacer

(window as any)[DynamicObject.key]
ago
fuente
3
Esto es así 2019.
Qwerty
4

Usando create-react-app v3.3, encontré que la forma más fácil de lograr esto era extender el Windowtipo en el autogenerado react-app-env.d.ts:

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

Primero debe declarar el objeto de ventana en el alcance actual.
Porque mecanografiado quisiera saber el tipo del objeto.
Como el objeto de ventana está definido en otro lugar, no puede redefinirlo.
Pero puede declararlo de la siguiente manera:

declare var window: any;

Esto no redefinirá el objeto de la ventana o no creará otra variable con nombre window.
Esto significa que la ventana está definida en otro lugar y solo está haciendo referencia a ella en el alcance actual.

Entonces puede referirse a su objeto MyNamespace simplemente por: -

window.MyNamespace

O puede establecer la nueva propiedad en el windowobjeto simplemente: -

window.MyNamespace = MyObject

Y ahora el mecanografiado no se quejará.

Mav55
fuente
¿puedo conocer su caso de uso de querer saber dónde windowse define?
Mav55
2

Quería usar esto en una biblioteca Angular (6) hoy y me tomó un tiempo lograr que funcionara como se esperaba.

Para que mi biblioteca use declaraciones, tuve que usar la d.tsextensión del archivo que declara las nuevas propiedades del objeto global.

Entonces, al final, el archivo terminó con algo como:

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

Una vez creado, no olvides exponerlo en tu public_api.ts.

Eso lo hizo por mí. Espero que esto ayude.

Puñal
fuente