Como atribuir propriedades dinamicamente a um objeto em TypeScript? Explicação clara para iniciantes
\n\nOlá, amigo! Se você já programou em JavaScript e depois migrou para TypeScript, certamente já enfrentou a situação de precisar adicionar uma nova propriedade a um objeto em tempo real. Em JS, isso se faz em dois segundos: obj.newProp = 'value'. Mas quando você tenta o mesmo em TypeScript, o compilador começa a reclamar: "Property 'newProp' does not exist on type ...". Familiar?
Não se preocupe, isso é normal. TypeScript nos protege de erros acidentais, mas às vezes seu rigor parece excessivo. Neste artigo, vamos analisar como atribuir propriedades dinamicamente a objetos de forma correta e segura. Você aprenderá várias maneiras comprovadas — desde as simples até as mais elegantes. Vamos lá!
\n\nPor que o TypeScript reclama?
\n\nAntes de resolver o problema, vamos entender sua raiz. TypeScript é uma extensão do JavaScript que adiciona tipagem estática. Quando você declara um objeto:
\n\nconst user = { name: 'Ana', age: 25 };\n\n\nTypeScript infere automaticamente seu tipo: { name: string; age: number }. Isso significa que o objeto user pode conter apenas essas duas propriedades. Tentar adicionar email causará um erro de compilação:
user.email = 'ana@exemplo.com'; // ❌ Erro!\n\n\nTypeScript não sabe que tal propriedade pode aparecer e bloqueia o código. Mas na vida real, muitas vezes precisamos adicionar propriedades dinamicamente — por exemplo, ao trabalhar com dados de APIs ou formulários. Vamos ver como fazer isso corretamente.
\n\nMétodo 1: O mais simples — as any
\n\nUma maneira rápida, mas suja, é usar as any. Ele literalmente diz ao TypeScript: "Desative a verificação de tipos para este objeto".
const user: any = { name: 'Ana', age: 25 };\nuser.email = 'ana@exemplo.com'; // ✅ Funciona\nuser.isAdmin = true; // ✅ Funciona\n\n\nPor que isso é ruim? Porque você perde todas as vantagens do TypeScript: autocompletar, verificação de tipos, proteção contra erros de digitação. Se você escrever acidentalmente user.emial, não haverá erro — você só descobrirá em tempo de execução. Use este método apenas para prototipagem rápida ou quando souber exatamente o que está fazendo.
Método 2: Assinatura de índice (Index Signature)
\n\nUma abordagem mais correta é declarar antecipadamente que o objeto pode conter propriedades dinâmicas. Para isso, usamos a assinatura de índice.
\n\ninterface User {\n name: string;\n age: number;\n [key: string]: any; // 👈 Permite quaisquer propriedades adicionais\n}\n\nconst user: User = { name: 'Ana', age: 25 };\nuser.email = 'ana@exemplo.com'; // ✅ Funciona\nuser.phone = '+55-11-99999-9999'; // ✅ Funciona\n\n\nO que está acontecendo aqui? A notação [key: string]: any significa: "o objeto pode ter quaisquer chaves de string, e seus valores podem ser de qualquer tipo". Agora você pode adicionar propriedades dinamicamente, e o TypeScript não reclamará.
Observação importante: se você quiser limitar os tipos de valores, substitua any por um tipo específico:
interface User {\n name: string;\n age: number;\n [key: string]: string | number; // Apenas strings e números\n}\n\n\nA assinatura de índice é uma excelente escolha quando você sabe antecipadamente que o objeto será expandido. Mas tenha cuidado: se a interface tiver outras propriedades com tipos diferentes, elas também devem corresponder à assinatura.
\n\nMétodo 3: Tipo utilitário Record
\n\nSe você precisar criar um objeto onde todas as chaves e valores são de um mesmo tipo, use o tipo embutido Record<Keys, Type>. Ele é ideal para dicionários ou mapeamentos.
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;\n\nconst roles: UserRoles = {\n ana: 'admin',\n joao: 'user',\n};\