Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/ts-event-handler-explicit-typing.md
1---2title: Always Explicitly Type Event Handlers3impact: MEDIUM4impactDescription: Without explicit typing, event parameters have implicit 'any' type causing TypeScript errors in strict mode5type: gotcha6tags: [vue3, typescript, events, dom-events, composition-api]7---89# Always Explicitly Type Event Handlers1011**Impact: MEDIUM** - Native DOM event handlers in Vue components have implicit `any` type for the `event` parameter. In TypeScript strict mode, this causes errors. You must explicitly type event parameters and use type assertions for `event.target`.1213## Task Checklist1415- [ ] Always type the `event` parameter explicitly (e.g., `Event`, `MouseEvent`)16- [ ] Use type assertions when accessing element-specific properties17- [ ] Consider using inline handlers for simple cases18- [ ] Be aware of Vue's synthetic event system1920## The Problem2122```vue23<script setup lang="ts">24// WRONG: event has implicit 'any' type25function handleChange(event) { // Error in strict mode!26console.log(event.target.value) // Also error: target might be null27}2829// WRONG: Missing type assertion for element access30function handleInput(event: Event) {31console.log(event.target.value) // Error: 'value' doesn't exist on EventTarget32}33</script>3435<template>36<input @change="handleChange" @input="handleInput" />37</template>38```3940## The Solution4142```vue43<script setup lang="ts">44// CORRECT: Explicit Event type + type assertion45function handleChange(event: Event) {46const target = event.target as HTMLInputElement47console.log(target.value)48}4950// CORRECT: Specific event type when needed51function handleClick(event: MouseEvent) {52console.log(event.clientX, event.clientY)53}5455function handleKeydown(event: KeyboardEvent) {56if (event.key === 'Enter') {57submitForm()58}59}6061function handleSubmit(event: SubmitEvent) {62event.preventDefault()63const formData = new FormData(event.target as HTMLFormElement)64}65</script>6667<template>68<input @change="handleChange" />69<button @click="handleClick">Click</button>70<input @keydown="handleKeydown" />71<form @submit="handleSubmit">...</form>72</template>73```7475## Common Event Types7677| Event | Type | Common Properties |78|-------|------|-------------------|79| click, dblclick | `MouseEvent` | clientX, clientY, button |80| keydown, keyup, keypress | `KeyboardEvent` | key, code, ctrlKey, shiftKey |81| input, change | `Event` | target (needs assertion) |82| focus, blur | `FocusEvent` | relatedTarget |83| submit | `SubmitEvent` | submitter |84| drag, dragstart, drop | `DragEvent` | dataTransfer |85| wheel, scroll | `WheelEvent` | deltaX, deltaY |86| touch events | `TouchEvent` | touches, changedTouches |8788## Element-Specific Type Assertions8990```vue91<script setup lang="ts">92// HTMLInputElement for text, number, checkbox, radio inputs93function handleTextInput(event: Event) {94const input = event.target as HTMLInputElement95console.log(input.value, input.checked)96}9798// HTMLSelectElement for select dropdowns99function handleSelect(event: Event) {100const select = event.target as HTMLSelectElement101console.log(select.value, select.selectedIndex)102}103104// HTMLTextAreaElement for textareas105function handleTextarea(event: Event) {106const textarea = event.target as HTMLTextAreaElement107console.log(textarea.value, textarea.selectionStart)108}109110// HTMLFormElement for forms111function handleFormSubmit(event: SubmitEvent) {112event.preventDefault()113const form = event.target as HTMLFormElement114const formData = new FormData(form)115}116</script>117```118119## Inline Event Handler Pattern120121For simple cases, inline handlers with type annotations work well:122123```vue124<template>125<!-- Inline with type assertion -->126<input127@input="(event: Event) => {128const input = event.target as HTMLInputElement129searchQuery = input.value130}"131/>132133<!-- Or with $event cast -->134<input @input="searchQuery = ($event.target as HTMLInputElement).value" />135</template>136```137138## Generic Handler Pattern139140Create reusable typed handlers:141142```typescript143// utils/events.ts144export function getInputValue(event: Event): string {145return (event.target as HTMLInputElement).value146}147148export function getSelectValue(event: Event): string {149return (event.target as HTMLSelectElement).value150}151152export function getCheckboxChecked(event: Event): boolean {153return (event.target as HTMLInputElement).checked154}155```156157```vue158<script setup lang="ts">159import { getInputValue, getCheckboxChecked } from '@/utils/events'160161const name = ref('')162const agreed = ref(false)163</script>164165<template>166<input @input="e => name = getInputValue(e)" />167<input type="checkbox" @change="e => agreed = getCheckboxChecked(e)" />168</template>169```170171## Vue Component Events172173For Vue component events (not DOM events), use `defineEmits` for type safety:174175```vue176<script setup lang="ts">177const emit = defineEmits<{178'custom-event': [data: { id: number; name: string }]179}>()180181// Handler for child component event182function handleChildEvent(data: { id: number; name: string }) {183console.log(data.id, data.name)184}185</script>186187<template>188<!-- Custom component event - properly typed -->189<ChildComponent @custom-event="handleChildEvent" />190</template>191```192193## Avoiding currentTarget vs target Confusion194195```typescript196function handleClick(event: MouseEvent) {197// target: The element that triggered the event (could be a child)198const target = event.target as HTMLElement199200// currentTarget: The element the listener is attached to201const currentTarget = event.currentTarget as HTMLButtonElement202203// Be explicit about which you need204if (target.tagName === 'SPAN') {205// Clicked on span inside button206}207}208```209210## Reference211- [Vue.js TypeScript with Composition API - Event Handlers](https://vuejs.org/guide/typescript/composition-api.html#typing-event-handlers)212- [MDN Event Reference](https://developer.mozilla.org/en-US/docs/Web/Events)213- [TypeScript DOM Types](https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts)214