«La propiedad 'value' no existe en el tipo 'EventTarget'» — cómo dejar de temerle y empezar a tipificar eventos en TypeScript
\n\n¿Alguna vez has escrito un manejador de eventos en TypeScript, intentaste obtener event.target.value — y de inmediato recibiste un subrayado rojo y el mensaje: «Property 'value' does not exist on type 'EventTarget'»? Si es así, no estás solo. En Stack Overflow, esta pregunta ha acumulado decenas de miles de visitas, y sigue atormentando a novatos y desarrolladores experimentados en 2025.
El problema no es que TypeScript sea «malo» o «esté roto». Todo lo contrario: el compilador te protege de errores tontos que podrían ocurrir en tiempo de ejecución. Analicemos por qué surge este error y cómo solucionarlo correctamente.
\n\n¿Por qué TypeScript se queja de event.target.value?
\n\nRespuesta breve: event.target tiene el tipo EventTarget | null. Y EventTarget no tiene la propiedad value. Solo tiene tres métodos: addEventListener, removeEventListener y dispatchEvent.
Cuando escribes:
\n\nconst handleChange = (event: React.ChangeEvent) => {\n const value = event.target.value; // ❌ ¡Error!\n};\n\n\nTypeScript ve: «Oye, no sé qué elemento HTML específico provocó este evento. Podría ser un <div>, que no tiene value, o un <input> — no estoy seguro. Por lo tanto, me niego a compilar este código».
Esto es una característica, no un error. TypeScript te obliga a indicar explícitamente con qué elemento estás trabajando.
\n\nTop 3 formas de solucionarlo (vigentes para 2025)
\n\nHay varios enfoques. Veamos los más populares y seguros.
\n\n1. Cast con as HTMLInputElement (rápido, pero con cuidado)
\n\nLa forma más común es convertir event.target al tipo deseado:
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = (event.target as HTMLInputElement).value;\n console.log(value);\n};\n\n\nVentajas: simple, claro, funciona.
\nDesventajas: si te equivocas y el evento realmente proviene de un <textarea> o <select>, obtendrás undefined en tiempo de ejecución. El compilador no te lo advertirá.
Es mejor usar este método cuando estés absolutamente seguro del tipo de elemento. Por ejemplo, en formularios simples con un solo campo.
\n\n2. Usar event.currentTarget en lugar de event.target
\n\nEn TypeScript, currentTarget está tipificado de forma más estricta que target. Si especificas el parámetro genérico para el evento, entonces currentTarget tendrá el tipo correcto:
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.currentTarget.value; // ✅ ¡Funciona sin cast!\n};\n\n\nImportante: currentTarget es el elemento al que está suscrito el manejador. Si te suscribes a un <input>, entonces currentTarget siempre será ese <input>. A diferencia de target, que puede ser un elemento anidado (por ejemplo, un <span> dentro de un botón).
Ventajas: más seguro que el cast, no requiere conversiones adicionales.
\nDesventajas: no es adecuado para todos los escenarios — por ejemplo, para eventos de propagación (bubbling), currentTarget puede no coincidir con el elemento objetivo.
3. Función genérica con verificación (la forma más segura)
\n\nSi deseas la máxima seguridad y reutilización, escribe una función auxiliar que verifique el tipo de elemento:
\n\nfunction getInputValue(event: Event): string