Skip to content
← Todas las preguntas
⚡ Intermedio

¿Cómo se tipan los manejadores de eventos en los templates de Vue?

🔷 TypeScript🧩 Componentes

Los manejadores de eventos DOM en templates de Vue reciben un objeto Event, pero TypeScript no sabe qué elemento específico lo disparó. Necesitas tipar el parámetro del evento explícitamente y hacer un cast de event.target al tipo de elemento correcto para acceder a propiedades como .value o .checked.

El problema

ts
// error en modo estricto: 'any' implícito
function handleInput(event) {
  console.log(event.target.value)
}

// error: 'value' no existe en EventTarget
function handleInput(event: Event) {
  console.log(event.target.value)
}

event.target tiene el tipo EventTarget | null, que no tiene la propiedad .value. Necesitas una aserción de tipo.

La solución

vue
<script setup lang="ts">
function handleInput(event: Event) {
  const target = event.target as HTMLInputElement
  console.log(target.value)
}

function handleClick(event: MouseEvent) {
  console.log(event.clientX, event.clientY)
}

function handleKeydown(event: KeyboardEvent) {
  if (event.key === 'Enter') {
    submit()
  }
}

function handleSubmit(event: SubmitEvent) {
  event.preventDefault()
  const form = event.target as HTMLFormElement
  const data = new FormData(form)
}
</script>

<template>
  <input @input="handleInput" />
  <button @click="handleClick">Click</button>
  <input @keydown="handleKeydown" />
  <form @submit="handleSubmit">...</form>
</template>
Open in Vue Playground

Referencia de tipos de evento

Evento en el templateTipo TypeScriptPropiedades clave
@click, @dblclickMouseEventclientX, clientY, button, ctrlKey
@keydown, @keyupKeyboardEventkey, code, ctrlKey, shiftKey
@input, @changeEventtarget (requiere cast)
@focus, @blurFocusEventrelatedTarget
@submitSubmitEventsubmitter
@drag, @dropDragEventdataTransfer
@wheelWheelEventdeltaX, deltaY
@touchstart, @touchendTouchEventtouches, changedTouches

Aserciones de tipo por elemento

Los distintos elementos de formulario necesitan casts diferentes:

ts
function handleTextInput(event: Event) {
  const input = event.target as HTMLInputElement
  console.log(input.value)
}

function handleCheckbox(event: Event) {
  const checkbox = event.target as HTMLInputElement
  console.log(checkbox.checked)
}

function handleSelect(event: Event) {
  const select = event.target as HTMLSelectElement
  console.log(select.value, select.selectedIndex)
}

function handleTextarea(event: Event) {
  const textarea = event.target as HTMLTextAreaElement
  console.log(textarea.value)
}

Manejadores inline

Para casos simples, haz el cast directamente en el template:

vue
<template>
  <input @input="name = ($event.target as HTMLInputElement).value" />
</template>
Open in Vue Playground

O usa una arrow function inline:

vue
<template>
  <input @input="(e: Event) => name = (e.target as HTMLInputElement).value" />
</template>
Open in Vue Playground

Eventos de componentes personalizados

Los eventos de componentes Vue (no eventos DOM) se tipan a través de defineEmits. No se necesita cast porque el tipo del payload lo define el hijo:

vue
<!-- ChildComponent.vue -->
<script setup lang="ts">
const emit = defineEmits<{
  select: [item: { id: number; name: string }]
}>()
</script>
Open in Vue Playground
vue
<!-- Parent.vue -->
<script setup lang="ts">
function handleSelect(item: { id: number; name: string }) {
  console.log(item.id) // completamente tipado
}
</script>

<template>
  <ChildComponent @select="handleSelect" />
</template>
Open in Vue Playground

target vs currentTarget

ts
function handleClick(event: MouseEvent) {
  // target: el elemento real que se clicó (puede ser un hijo)
  const target = event.target as HTMLElement

  // currentTarget: el elemento al que está asociado el listener
  const button = event.currentTarget as HTMLButtonElement
}

Si tienes un botón con un span dentro, al clicar el span, target es el span y currentTarget es el botón. Usa currentTarget cuando quieras el elemento al que está adjunto el manejador.

Ver también: ¿Cómo emitir eventos con TypeScript? · ¿Cómo funciona el manejo de eventos?

Referencias

Publicado bajo la licencia MIT.