Introdução: por que o tópico object literal ainda explode no Stack Overflow?
\n\nSe você já escreveu em TypeScript, certamente já se deparou com esta situação: cria um objeto, especifica o tipo, e o compilador dá erro ou, pior ainda, não dá erro, mas em tempo de execução tudo quebra. Familiar?
\n\nO tópico type definition in object literal in TypeScript é uma das consultas mais populares no Stack Overflow com milhares de visualizações. E não é por acaso. A aparente simplicidade esconde muitas nuances: a diferença entre interface e type, propriedades excessivas, campos opcionais, modificadores readonly e o novo operador satisfies.
Em 2025, o TypeScript avançou muito. Surgiram recursos que tornam o código mais limpo e seguro. Mas muitos desenvolvedores continuam usando abordagens ultrapassadas. Vamos entender como tipificar corretamente literais de objeto hoje, para que o código seja robusto, legível e moderno.
\n\n1. Interface ou Type: o que escolher para object literal em 2025?
\n\nEsta é uma pergunta clássica que gera debates acalorados. Vamos esclarecer de uma vez.
\n\nQuando usar interface
\nInterface é o "padrão ouro" para descrever a forma de objetos. Funciona melhor com inferência de tipos, fornece mensagens de erro mais claras e suporta declaration merging (fusão de declarações).
\n\n// ✅ Bom: interface para object literal\ninterface User {\n name: string;\n age: number;\n email?: string; // campo opcional\n}\n\nconst user: User = {\n name: "Ana",\n age: 28,\n email: "ana@example.com"\n};\n\nQuando usar type
\nType aliases são indispensáveis para union types, intersection types, tuplas e tipos condicionais complexos. Para objetos simples também funcionam, mas têm limitações.
\n\n// ✅ Bom: type para união\ntype Status = "active" | "inactive" | "pending";\n\n// type para objeto também ok, mas menos extensível\ntype UserType = {\n name: string;\n age: number;\n};\n\nDica prática 2025: Use interface para APIs públicas e descrição de formas de objetos. type — para composições complexas, tipos condicionais e union types. Não é uma regra absoluta, mas uma boa prática.
2. Propriedades excessivas (excess property checks): inimigo ou amigo?
\n\nUm dos erros mais comuns de iniciantes é uma propriedade extra em um literal de objeto. O TypeScript reclama, e isso é bom.
\n\ninterface Config {\n url: string;\n timeout?: number;\n}\n\n// ❌ Erro: propriedade extra 'retries'\nconst badConfig: Config = {\n url: "https://api.example.com",\n timeout: 5000,\n retries: 3 // Error!\n};\n\nMas há uma brecha: se você atribuir o objeto através de uma variável, a verificação é mais branda. Fique atento.
\n\nconst configData = {\n url: "https://api.example.com",\n timeout: 5000,\n retries: 3\n};\n\n// ✅ Sem erro (mas pode ser inesperado)\nconst config: Config = configData;\n\nEm 2025, use satisfies para uma verificação rigorosa sem perder a inferência de tipos. Sobre isso, no próximo tópico.
3. Operador satisfies: o principal recurso de 2025
\n\nO operador satisfies surgiu no TypeScript 4.9 e foi um verdadeiro avanço. Ele permite verificar se o tipo de uma expressão satisfaz um determinado tipo, sem alterar o próprio tipo inferido.
// Abordagem antiga: perde o tipo exato\nconst palette: Record<string, string | string[]> = {\n red: [255, 0, 0],\n green: "#00ff00",\n blue: "#0000ff"\n};\n// palette.red — string | string[] (ruim)\n\n// Nova abordagem com satisfies\nconst palette = {\n red: [255, 0, 0],\n green: "#00ff00",\n blue: "#0000ff"\n} satisfies Record<string, string | string[]>;\n\n// palette.red — number[] (tipo exato preservado!)\n// palette.green — string\n// palette.blue — string