«Property 'value' does not exist on type 'EventTarget'» — como parar de temer e começar a tipar eventos no TypeScript
\n\nVocê já escreveu um manipulador de eventos no TypeScript, tentou obter event.target.value — e imediatamente recebeu um sublinhado vermelho e a mensagem: «Property 'value' does not exist on type 'EventTarget'»? Se sim — você não está sozinho. No Stack Overflow, esta pergunta acumulou dezenas de milhares de visualizações e continua a atormentar novatos e desenvolvedores experientes em 2025.
O problema não é que o TypeScript seja "malvado" ou "quebrado". Muito pelo contrário: o compilador protege você de erros bobos que poderiam ocorrer em tempo de execução. Vamos entender por que esse erro ocorre e como corrigi-lo corretamente.
\n\nPor que o TypeScript reclama de event.target.value?
\n\nResposta curta: event.target tem o tipo EventTarget | null. E EventTarget não possui a propriedade value. Ele tem apenas três métodos: addEventListener, removeEventListener e dispatchEvent.
Quando você escreve:
\n\nconst handleChange = (event: React.ChangeEvent) => {\n const value = event.target.value; // ❌ Erro!\n};\n\n\nO TypeScript vê: "Ei, eu não sei qual elemento HTML específico disparou este evento. Pode ser uma <div>, que não tem value, ou pode ser um <input> — não tenho certeza. Portanto, recuso-me a compilar este código".
Isso é uma característica, não um bug. O TypeScript força você a especificar explicitamente com qual elemento está trabalhando.
\n\nTop 3 formas de resolver (relevante para 2025)
\n\nExistem várias abordagens. Vamos ver as mais populares e seguras.
\n\n1. Cast com as HTMLInputElement (rápido, mas com cuidado)
\n\nA forma mais comum é fazer um cast de event.target para o tipo desejado:
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = (event.target as HTMLInputElement).value;\n console.log(value);\n};\n\n\nPrós: simples, claro, funciona.
\nContras: se você se enganar e o evento vier de uma <textarea> ou <select>, receberá undefined em tempo de execução. O compilador não avisará.
É melhor usar este método quando você tem absoluta certeza do tipo do elemento. Por exemplo, em formulários simples com um único campo.
\n\n2. Usar event.currentTarget em vez de event.target
\n\nNo TypeScript, currentTarget é tipado de forma mais estrita que target. Se você especificar o parâmetro genérico para o evento, currentTarget terá o tipo correto:
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.currentTarget.value; // ✅ Funciona sem cast!\n};\n\n\nImportante: currentTarget é o elemento ao qual o manipulador está vinculado. Se você vinculou a um <input>, currentTarget sempre será esse <input>. Diferente de target, que pode ser um elemento aninhado (por exemplo, um <span> dentro de um botão).
Prós: mais seguro que cast, não requer conversões extras.
\nContras: não é adequado para todos os cenários — por exemplo, para eventos de propagação (bubbling), currentTarget pode não coincidir com o elemento alvo.
3. Função genérica com verificação (a forma mais segura)
\n\nSe você deseja máxima segurança e reutilização, escreva uma função auxiliar que verifique o tipo do elemento:
\n\nfunction getInputValue(event: Event): string