Como você define explicitamente uma nova propriedade em `window` no TypeScript?

621

Eu configuro namespaces globais para meus objetos definindo explicitamente uma propriedade window.

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

O TypeScript sublinha MyNamespacee reclama que:

A propriedade 'MyNamespace' não existe no valor do tipo 'window' any "

Posso fazer o código funcionar declarando MyNamespacecomo uma variável de ambiente e eliminando a windowexplicitação, mas não quero fazer isso.

declare var MyNamespace: any;

MyNamespace = MyNamespace || {};

Como posso ficar windowlá e fazer o TypeScript feliz?

Como observação lateral, acho especialmente engraçado que o TypeScript se queixe, pois me diz que windowé do tipo anyque definitivamente pode conter qualquer coisa.

joshuapoehls
fonte
veja Link
MHS

Respostas:

692

Acabei de encontrar a resposta para isso na resposta de outra pergunta do StackOverflow .

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

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

Basicamente, você precisa estender a windowinterface existente para informar sobre sua nova propriedade.

joshuapoehls
fonte
12
Observe o capital W na janela. Isso me tropeçou.
AJM
2
Não consegui compilar isso com o tsc 1.0.1.0. A resposta de Blake Mitchell funcionou para mim, no entanto.
Pat
43
Esteja ciente de que isso só funciona quando declarado em um .d.tsarquivo separado . Não funciona quando usado em um .tsarquivo que usa as importações e as exportações. Veja esta resposta .
Cdauth 9/03/16
36
Os declare global { interface Window { ... } }trabalhos com texto datilografado 2.5.2, não há necessidade de .d.tsarquivo como mencionado acima
tanguy_k
2
No começo, o texto datilografado me dizia: error TS1234: An ambient module declaration is only allowed at the top level in a file.quando eu o colocava na parte superior do arquivo, ele corrigia esse erro; portanto, talvez você precise fazer isso também.
Zelphir Kaltstahl
397

Para mantê-lo dinâmico, basta usar:

(<any>window).MyNamespace
chinupson
fonte
9
Alguma idéia de como fazer isso em um arquivo tsx?
velop
2
@martin: O <any> torna explícito, então funciona muito bem, acredito. Outros: Se você estiver usando Typescript com Reagir ou outro ambiente JSX (resultando em sintaxe TSX) você terá que usar asem vez de <>. Veja a resposta de @ david-boyd abaixo .
Don
31
Eu tive que usar (janela como qualquer) .MyNamespace
Arsalan Ahmad
Da mesma forma para this-return (<any> this)['something in scope']
HankCa
1
Eu tive que desativar o tslint nessa linha com/* tslint:disable */
Michael Yagudaev 16/02/19
197

A PARTIR DO TIPESCRIÇÃO ^ 3.4.3 ESTA SOLUÇÃO NÃO FUNCIONA MAIS

Ou...

você pode apenas digitar:

window['MyNamespace']

e você não receberá um erro de compilação e funciona da mesma maneira que digitar window.MyNamespace

Evan Larsen
fonte
15
mas você provavelmente irá obter um erro tslint ... Se você tem um claro
smnbbrv
69
Isso é completamente contrário à digitação forte, toda a ideia por trás do TypeScript.
D512 28/01
18
@ user1334007 usando globals também. No entanto, algum código legado exige isso.
Nathancahill 01/03
3
@Ramesh window['MyNamespace']()(basta adicionar colchetes)
iBaff
6
"Isso é completamente contrário à digitação forte, toda a idéia por trás do TypeScript.". BOA!
Cody
188

Usando TSX? Nenhuma das outras respostas estava funcionando para mim.

Aqui está o que eu fiz:

(window as any).MyNamespace
David Boyd
fonte
4
Mesmo que (<any> window).MyNamespace, na verdade,
Dmitry Parzhitsky
44
É o mesmo, exceto ao usar o TSX, porque o <any>é interpretado como JSX, não um tipo de conversão.
Jake Boone
1
Obrigado @ David, isso funcionou como um encanto com o novo aplicativo Create React e versão datilografada. Anteriormente, isso estava funcionando: (<qualquer> janela) .MyNamespace, mas agora está quebrando a nova versão datilografada 3.5.x.
Jignesh Raval
janela como qualquer? Então, por que você usaria texto datilografado? Basta usar Javascript x)
MarcoLe 19/03
71

A resposta aceita é o que eu costumava usar, mas com o TypeScript 0.9. * Ele não funciona mais. A nova definição da Windowinterface parece substituir completamente a definição interna, em vez de aumentá-la.

Eu comecei a fazer isso:

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

declare var window: MyWindow;

ATUALIZAÇÃO: Com o TypeScript 0.9.5, a resposta aceita está funcionando novamente.

Blake Mitchell
fonte
2
Isso funciona também com os módulos usados ​​pelo TypeScript 2.0.8. Exemplo: export default class MyClass{ foo(){ ... } ... } interface MyWindow extends Window{ mc: MyClass } declare var window: MyWindow window.mc = new MyClass() você pode chamar foo (), por exemplo, no console do Chrome Dev Tools, como mc.foo()
Martin Majewski
Essa é uma resposta muito boa se você não quiser declarar algo como global. Por outro lado, você precisa chamar declare var...todos os arquivos necessários.
Puce 25/05
Mais uma para essa abordagem, porque neste caso você não entra em conflito com os outros pacotes que estendem a janela global no monorepo.
Dmitrii Sorin
Obrigado! Melhor resposta IMO. Não se deve substituir Windowpelo simples fato de que não é uma janela de baunilha. Apenas contornar a verificação de tipos ou tentar enganar o TS também não é o caminho para fazê-lo.
Rutger Willems
Isso não funciona a partir de TS 3.6, infelizmente
Jacob Soderlund
65

Global são "maus" :), acho que a melhor maneira de ter também a portabilidade é:

Primeiro você exporta a interface: (por exemplo: ./custom.window.ts)

export interface CustomWindow extends Window {
    customAttribute: any;
}

Segundo você importa

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

Terceira janela de var global convertida com CustomWindow

declare let window: CustomWindow;

Dessa forma, você também não tem linha vermelha no IDE diferente se usar com atributos existentes do objeto window, portanto, no final, tente:

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

Testado com o Typecript 2.4.xe mais recente!

onalbi
fonte
1
você pode querer expandir por que os globais são maus. No nosso caso, estamos usando uma API não padronizada no objeto window. Por enquanto é polyfilled. Isso é verdadeiramente mau?
Mathijs Segers
1
@MathijsSegers eu não conheço especialmente o seu caso, mas .. sobre globals são maus não é apenas sobre javascript .. está em todos os idiomas .. algumas razões são: - Você não pode aplicar um bom padrão de design - Problema de memória e desempenho (você tê-los em todos os lugares, tente anexar algo ao String Object e veja o que acontece quando você faz uma nova String) - Colisão de nomes causando efeito colateral no código .. (especialmente em javascript que têm natureza assíncrona) Posso continuar, mas acho que você pode imaginar o rest ...
onalbi 7/11
1
@onabi Estou literalmente falando sobre um recurso do navegador. Está disponível globalmente, pois é o navegador. Você realmente sugere que ainda é errado procurar definições globais? Quero dizer, poderíamos implementar o Ponyfills, mas isso incharia os navegadores que realmente suportam o recurso (por exemplo, API de tela cheia). Dito isto, não acredito que todos os globais são maus.
Mathijs Segers
2
@MathijsSegers na minha opinião, a variável global deve ser evitada para ser usada ou modificada onde pudermos .. é verdade que a janela está disponível porque o navegador existe, mas também é verdade que estava em mutação o tempo todo. Então, por exemplo, definimos agora window.feature = 'Recurso'; e isso é usado de maneira maciça no código .. o que acontece se window.feature é adicionado pelos navegadores em todo o código que esse recurso substitui .. de qualquer maneira eu dei uma explicação da minha frase para não ir contra você .. no que diz respeito ...
Onalbi
43

Para aqueles que usam a CLI Angular , é simples:

src / polyfills.ts

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

my-custom-utils.ts

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

Se você estiver usando o IntelliJ, também precisará alterar a seguinte configuração no IDE antes de seus novos polyfills:

> File 
> Settings 
> Languages & Frameworks 
> TypeScript 
> check 'Use TypeScript Service'.
Stephen Paul
fonte
1
declare globalé o truque, e esta resposta não é realmente específica para o Angular CLI ...
Avindra Goolcharan
31

Não preciso fazer isso com muita frequência, o único caso que tive foi ao usar o Redux Devtools com middleware.

Eu simplesmente fiz:

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

Ou você pode fazer:

let myWindow = window as any;

e depois myWindow.myProp = 'my value';

Dimitar Nikovski
fonte
1
Nesse caso, você também pode instalar o pacote npm, que contém todas as definições - conforme especificado nos documentos
bbrinx
Você pode fazer isso, mas essa não é a resposta para a pergunta, mas sim uma solução alternativa
Vitalij
Graças a isso, eu pude habilitar as ferramentas de redux corretamente no texto datilografado usando (window any any) .__ REDUX_DEVTOOLS_EXTENSION__ && (window any any) .__ REDUX_DEVTOOLS_EXTENSION __ ())
danivicario
20

A maioria das outras respostas não é perfeita.

  • Alguns deles apenas suprimem a inferência de tipo para a loja.
  • Alguns dos outros se preocupam apenas com a variável global como espaço para nome, mas não como interface / classe

Eu também encontro o mesmo problema esta manhã. Eu tentei tantas "soluções" no SO, mas nenhuma delas produz nenhum erro de tipo absolutamente e habilita o disparo de tipo de salto no IDE (webstorm ou vscode).

Finalmente, daqui

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

, Acho uma solução razoável para anexar digitações para a variável global que atua como interface / classe e espaço para nome .

O exemplo está abaixo:

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

declare interface MyNamespace {
    somemethod?()
}

declare namespace MyNamespace {
    // ...
}

Agora, o código acima mescla as tipagens do namespace MyNamespacee da interface MyNamespacena variável global myNamespace(a propriedade da janela).

e-cloud
fonte
2
Obrigado - este é o único que você pode aparentemente usar em um contexto de ambiente que não seja o módulo.
Tyler Sebastian
13

Veja como fazer isso, se você estiver usando o TypeScript Definition Manager !

npm install typings --global

Criar typings/custom/window.d.ts:

interface Window {
  MyNamespace: any;
}

declare var window: Window;

Instale sua digitação personalizada:

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

Feito, use‌ ! Texto datilografado não vai mais reclamar:

window.MyNamespace = window.MyNamespace || {};
Nik Sumeiko
fonte
1
O FWIW typingsficou obsoleto com o Typescript 2.0 (meados de 2016) e foi arquivado pelo proprietário.
Mrm
13

O Typscript não executa verificação de tipo nas propriedades da string.

window["newProperty"] = customObj;

Idealmente, o cenário variável global deve ser evitado. Às vezes, uso-o para depurar um objeto no console do navegador.

Irshad
fonte
8

Se você estiver usando o Typecript 3.x, poderá omitir a declare globalparte das outras respostas e, em vez disso, basta usar:

interface Window {
  someValue: string
  another: boolean
}

Isso funcionou comigo ao usar o Typecript 3.3, WebPack e TSLint.

Dana Woodman
fonte
Não funciona ^3.4.3. Esta resposta funcionou para mim stackoverflow.com/a/42841166/1114926
Verde
7

Para referência (esta é a resposta correta):

Dentro de um .d.tsarquivo de definição

type MyGlobalFunctionType = (name: string) => void

Se você trabalha no navegador, adiciona membros ao contexto da janela do navegador reabrindo a interface do Windows:

interface Window {
  myGlobalFunction: MyGlobalFunctionType
}

Mesma idéia para o NodeJS:

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

Agora você declara a variável raiz (que realmente ficará na janela ou global)

declare const myGlobalFunction: MyGlobalFunctionType;

Em seguida, em um .tsarquivo regular , mas importado como efeito colateral, você realmente o implementa:

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

E, finalmente, use-o em outro lugar na base de código, com:

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

myGlobalFunction("Kevin");
Benoit B.
fonte
Obrigado, este é o melhor exemplo que encontrei e funcionando bem.
Nick G.
6

Faça uma interface personalizada estender a Janela e adicione sua propriedade personalizada como opcional.

Em seguida, deixe o CustomWindow que usa a interface personalizada, mas avaliado com a janela original.

Trabalhou com o [email protected].

interface ICustomWindow extends Window {
  MyNamespace?: any
}

const customWindow:ICustomWindow = window;

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

Para aqueles que desejam definir uma propriedade computada ou dinâmica no windowobjeto, você verá que isso não é possível com o declare globalmétodo. Para esclarecer esse caso de uso

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

Você pode tentar fazer algo assim

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

O erro acima irá embora. Isso ocorre porque no Typescript, as interfaces não funcionam bem com as propriedades computadas e geram um erro como

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

Para contornar isso, você pode sugerir a transmissão windowpara <any>que você possa fazer

(window as any)[DynamicObject.key]
agosto
fonte
3
É assim que 2019.
Qwerty
4

Usando create-react-app v3.3, encontrei a maneira mais fácil de conseguir isso: estender o Windowtipo no arquivo gerado automaticamente react-app-env.d.ts:

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

Primeiro, você precisa declarar o objeto da janela no escopo atual.
Porque o texto datilografado gostaria de saber o tipo do objeto.
Como o objeto window está definido em outro lugar, você não pode redefini-lo.
Mas você pode declarar da seguinte forma: -

declare var window: any;

Isso não redefinirá o objeto da janela ou não criará outra variável com o nome window.
Isso significa que a janela está definida em outro lugar e você está apenas referenciando-a no escopo atual.

Em seguida, você pode se referir ao seu objeto MyNamespace simplesmente:

window.MyNamespace

Ou você pode definir a nova propriedade no windowobjeto simplesmente por: -

window.MyNamespace = MyObject

E agora o texto datilografado não vai reclamar.

Mav55
fonte
posso saber o seu caso de uso de querer saber onde windowestá definido?
Mav55
2

Eu queria usar isso em uma biblioteca Angular (6) hoje e demorei um pouco para que isso funcionasse conforme o esperado.

Para que minha biblioteca use declarações, tive que usar a d.tsextensão do arquivo que declara as novas propriedades do objeto global.

Então, no final, o arquivo acabou com algo como:

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

Uma vez criado, não esqueça de expô-lo no seu public_api.ts.

Isso fez por mim. Espero que isto ajude.

Dirk
fonte