Introdução: Por que adicionar uma propriedade ao window é uma dor de cabeça para todo desenvolvedor TypeScript?
Imagine a situação: você está escrevendo uma aplicação SPA moderna em TypeScript. Tudo vai bem, até que surge a necessidade de salvar um estado global ou integrar com um script externo (por exemplo, analytics, mapas ou gateway de pagamento). Você escreve:
window.myAppConfig = { apiKey: '123' };E então o TypeScript mostra um erro vermelho: "Property 'myAppConfig' does not exist on type 'Window & typeof globalThis'". Familiar? Essa pergunta já acumulou milhares de visualizações no Stack Overflow, e hoje não vamos apenas dar uma solução pronta, mas também olhar sob o capô do TypeScript para entender por que isso acontece e como lidar com isso de forma elegante e segura.
Neste artigo, vamos abordar várias abordagens — desde "gambiarras" para iniciantes até padrões profissionais usados por desenvolvedores seniores. Vamos lá!
Sob o capô: por que o TypeScript reclama de window.myProp?
Para entender o problema, precisamos lembrar que o TypeScript é uma extensão do JavaScript que adiciona tipagem estática. O objeto window no navegador é um objeto global que possui um tipo predefinido (Window). Esse tipo está descrito nas bibliotecas padrão do TypeScript (lib.dom.d.ts) e inclui todas as propriedades padrão: document, location, setTimeout e centenas de outras.
Quando você tenta atribuir window.myProp, o TypeScript verifica: "Essa propriedade existe na interface Window?". Não — então é erro. Isso é uma proteção contra erros de digitação e poluição acidental do escopo global. Mas o que fazer se você intencionalmente quiser estender o window?
Método 1: O mais simples, mas perigoso — as any
A primeira opção que vem à mente de um iniciante é simplesmente "calar" o TypeScript usando uma conversão de tipo:
(window as any).myAppConfig = { apiKey: '123' };Vantagens: rápido, funciona, não requer definições adicionais.
Desvantagens: você perde todas as vantagens do TypeScript. Sem autocomplete, sem verificação de tipos. Se você acidentalmente escrever myAppConfigg — não haverá erro, mas a aplicação quebrará em tempo de execução. É como desligar os airbags do carro — dá para dirigir, mas é arriscado.
Quando usar: apenas para prototipagem rápida ou se você tiver certeza de que a propriedade será usada uma única vez e depois removida.
Método 2: Estendendo a interface global (Interface Merging)
O TypeScript suporta fusão de interfaces (declaration merging). Este é o mecanismo chave que permite "adicionar" novas propriedades ao tipo Window já existente. Para isso, você precisa declarar uma interface com o mesmo nome no escopo global.
Crie um arquivo global.d.ts (ou adicione em qualquer arquivo .d.ts do seu projeto):
// global.d.tsinterface Window { myAppConfig: { apiKey: string; debugMode?: boolean; }; myAnalyticsFunction: (event: string, data: object) => void;}Agora você pode escrever:
window.myAppConfig = { apiKey: '123' };window.myAnalyticsFunction('page_view', { page: '/home' });
// TypeScript sugerirá que apiKey é uma stringconsole.log(window.myAppConfig.apiKey); // ✅ Sem errosComo funciona sob o capô? O TypeScript automaticamente mescla (merge) todas as declarações de interfaces com o mesmo nome. Se em lib.dom.d.ts já existe interface Window { ... }, sua interface com o mesmo nome simplesmente adicionará novas propriedades a ela. Isso é chamado de glob