TypeScript Function Overloading: por que isso não é "mágica", mas uma ferramenta para código inteligente
\n\nSe você já escreveu em JavaScript uma função que aceita "uma string, um número, ou até um array", conhece aquele momento desagradável: dentro da função — um inferno de ifs, e do lado de fora — total incerteza para quem chama a função. O TypeScript resolve esse problema de forma elegante, mas nem sempre óbvia. A sobrecarga de funções (function overloading) não é sobre criar várias funções com o mesmo nome (como em Java ou C++). É sobre declarar várias assinaturas para uma única implementação.
Em 2025, quando o TypeScript se tornou o padrão de facto para projetos sérios de frontend e backend, saber configurar a sobrecarga corretamente não é um "diferencial", mas uma habilidade que torna seu código legível, type-safe e fácil para o time. Vamos entender como isso funciona, onde os iniciantes costumam errar e quais padrões vale a pena adotar.
\n\nO que é sobrecarga de funções na prática?
\n\nImagine que você está escrevendo uma função formatDate. Ela precisa ser capaz de:
- \n
- Aceitar um objeto
Datee retornar uma string. \n - Aceitar uma string no formato ISO e retornar uma string. \n
- Aceitar um número (timestamp) e retornar uma string. \n
Se você simplesmente escrever function formatDate(input: Date | string | number): string, o TypeScript não conseguirá sugerir qual tipo exato você está passando em uma chamada específica. A IDE mostrará um tipo genérico, e você perderá o autocomplete. A sobrecarga resolve isso:
// Assinaturas de sobrecarga (overload signatures)\nfunction formatDate(input: Date): string;\nfunction formatDate(input: string): string;\nfunction formatDate(input: number): string;\n\n// Implementação (implementation signature)\nfunction formatDate(input: Date | string | number): string {\n if (input instanceof Date) {\n return input.toISOString();\n } else if (typeof input === 'string') {\n return new Date(input).toISOString();\n } else {\n return new Date(input).toISOString();\n }\n}\n\nAgora, quando você chama formatDate(new Date()), o TypeScript sabe que o retorno é string e sugere os tipos corretamente. Não há mágica — há tipagem estrita.
Regra nº1: assinaturas de sobrecarga não podem ser implementações
\n\nO erro mais comum de iniciantes é tentar escrever o corpo da função em cada assinatura. Não faça isso:
\n\n// ❌ Erro: duplicação de implementação\nfunction foo(x: number): number {\n return x * 2;\n}\nfunction foo(x: string): string {\n return x.toUpperCase();\n}\n\nO TypeScript não suporta múltiplas implementações com o mesmo nome. Sempre uma implementação, várias declarações.
\n\nRegra nº2: a ordem das assinaturas importa
\n\nO TypeScript verifica as assinaturas de cima para baixo. A primeira assinatura compatível determina o tipo de retorno. Portanto:
\n- \n
- Coloque tipos mais específicos primeiro (ex.:
stringantes deany). \n - Evite assinaturas sobrepostas que possam confundir o compilador. \n
// Bom: primeiro o caso específico\nfunction createButton(label: string, disabled?: boolean): HTMLButtonElement;\nfunction createButton(options: { label: string; disabled?: boolean }): HTMLButtonElement;\nfunction createButton(labelOrOptions: string | { label: string; disabled?: boolean }, disabled?: boolean): HTMLButtonElement {\n // implementação\n}\n\nRegra nº3: use Union Types se tiver mais de 3 assinaturas
\n\nA sobrecarga é uma ferramenta poderosa, mas não abuse dela. Se você tem 5 ou mais assinaturas, o código se torna ilegível. Em 2025, a boa prática é:
\n- \n
- Até 3 assinaturas — use sobrecarga. \n
- Mais de 3 — considere usar
Union Typeou uma