Como sobrescrever o tipo de propriedade de interface de um arquivo .d.ts no TypeScript: Guia completo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que você não pode simplesmente pegar e alterar o tipo?

\n\n

Imagine a situação: você está integrando uma biblioteca poderosa para trabalhar com gráficos. Ela tem documentação impecável, centenas de estrelas no GitHub e até arquivos .d.ts com descrições de tipos inclusos. Mas há um espinho — o desenvolvedor da biblioteca decidiu que o campo data deve ser string | number, enquanto você precisa estritamente de Date. Você vai até node_modules, altera o arquivo — e tudo quebra no próximo npm install.

\n\n

Familiar? Este é um dos problemas mais comuns enfrentados por desenvolvedores TypeScript. A pergunta «Overriding interface property type defined in Typescript d.ts file» acumulou milhares de visualizações no Stack Overflow. E não é à toa — o diabo está nos detalhes.

\n\n

Neste artigo, vamos abordar 5 maneiras legais de sobrescrever o tipo de propriedade de uma interface de um arquivo .d.ts externo. Sem hacks de copiar arquivos — apenas TypeScript puro.

\n\n

Entendendo o problema: por que o TypeScript resiste?

\n\n

Antes de quebrar o sistema, vamos entender sua lógica. O TypeScript é rigoroso com tipos não por maldade, mas pela sua segurança. Quando você tenta sobrescrever uma propriedade de interface, está essencialmente dizendo: «Eu sei melhor que o autor da biblioteca». E o TypeScript pergunta: «Tem certeza? Prove».

\n\n

Vamos considerar um exemplo clássico. Suponha que temos uma interface de uma biblioteca:

\n\n
// node_modules/some-library/types.d.ts\nexport interface UserConfig {\n  id: string | number;\n  name: string;\n  createdAt: Date | string;\n  metadata: Record<string, unknown>;\n}
\n\n

Você quer que createdAt seja apenas Date. A herança simples não funcionará — o TypeScript emitirá um erro se você tentar restringir o tipo. Mas existem soluções alternativas.

\n\n

Método 1: Declaration Merging (fusão de declarações)

\n\n

A maneira mais elegante, quando possível. Se a biblioteca exporta uma interface, e não um tipo (type), você pode «complementá-la» no escopo global ou em um módulo.

\n\n

Condição importante: interfaces com o mesmo nome no mesmo escopo são automaticamente mescladas. Mas não é possível sobrescrever uma propriedade existente — apenas adicionar uma nova.

\n\n

Portanto, este método é adequado quando você precisa estender a interface, e não alterar um campo existente. Por exemplo:

\n\n
// global.d.ts\nexport interface UserConfig {\n  customField: boolean; // ✅ Adicionamos um novo campo\n}\n\n// Mas assim — erro:\nexport interface UserConfig {\n  createdAt: Date; // ❌ Erro: duplicação de identificador\n}
\n\n

Conclusão: Declaration Merging é uma ótima ferramenta para adicionar campos, mas inútil para sobrescrever os existentes.

\n\n

Método 2: Usando Omit e uma interface própria

\n\n

Esta é a bala de prata para 90% dos casos. A ideia é simples: «recortamos» a propriedade problemática usando Omit e criamos um novo tipo com o tipo desejado.

\n\n
// Seu código\ntype MyUserConfig = Omit<UserConfig, 'createdAt'> & {\n  createdAt: Date; // ✅ Agora apenas Date\n};\n\n// Uso\nconst config: MyUserConfig = {\n  id: 42,\n  name: 'Alice',\n  createdAt: new Date('2024-01-01'), // Funciona\n  metadata: { role: 'admin' }\n};
\n\n

O que acontece aqui? Omit<UserConfig, 'createdAt'> cria um tipo sem o campo createdAt. Em seguida, através da interseção de tipos (&), adicionamos este campo de volta, mas com o tipo desejado.

\n\n

Vantagens: ideal para alterações pontuais, não afeta a interface original.
\nDesvantagens: se houver muitas propriedades, o código se torna verboso.

\n\n

Método 3: Extensão com Pick e Partial

\n\n

Às vezes, é necessário sobrescrever não uma, mas várias propriedades. Ou você quer tornar todos os campos opcionais,

Blogs

Book Recommendations