All skills
hyf0 avatar

/vue-debug-guides

@a5fc891 official
by hyf0hyf0/vue-skills2.9k stars
167

Vue 3 debugging and error handling for runtime errors, warnings, async failures, and SSR/hydration issues. Use when diagnosing or fixing Vue issues.

Use this Skill: https://skilld.dev/gh/hyf0/vue-skills/vue-debug-guides

This session only. Nothing lands on disk.

referencets-event-handler-explicit-typing.md

≈1.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Always Explicitly Type Event Handlers

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.

Task Checklist

  • Always type the event parameter explicitly (e.g., Event, MouseEvent)
  • Use type assertions when accessing element-specific properties
  • Consider using inline handlers for simple cases
  • Be aware of Vue's synthetic event system

The Problem

<script setup lang="ts">
// WRONG: event has implicit 'any' type
function handleChange(event) {  // Error in strict mode!
  console.log(event.target.value)  // Also error: target might be null
}

// WRONG: Missing type assertion for element access
function handleInput(event: Event) {
  console.log(event.target.value)  // Error: 'value' doesn't exist on EventTarget
}
</script>

<template>
  <input @change="handleChange" @input="handleInput" />
</template>

The Solution

<script setup lang="ts">
// CORRECT: Explicit Event type + type assertion
function handleChange(event: Event) {
  const target = event.target as HTMLInputElement
  console.log(target.value)
}

// CORRECT: Specific event type when needed
function handleClick(event: MouseEvent) {
  console.log(event.clientX, event.clientY)
}

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

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

<template>
  <input @change="handleChange" />
  <button @click="handleClick">Click</button>
  <input @keydown="handleKeydown" />
  <form @submit="handleSubmit">...</form>
</template>

Common Event Types

Event Type Common Properties
click, dblclick MouseEvent clientX, clientY, button
keydown, keyup, keypress KeyboardEvent key, code, ctrlKey, shiftKey
input, change Event target (needs assertion)
focus, blur FocusEvent relatedTarget
submit SubmitEvent submitter
drag, dragstart, drop DragEvent dataTransfer
wheel, scroll WheelEvent deltaX, deltaY
touch events TouchEvent touches, changedTouches

Element-Specific Type Assertions

<script setup lang="ts">
// HTMLInputElement for text, number, checkbox, radio inputs
function handleTextInput(event: Event) {
  const input = event.target as HTMLInputElement
  console.log(input.value, input.checked)
}

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

// HTMLTextAreaElement for textareas
function handleTextarea(event: Event) {
  const textarea = event.target as HTMLTextAreaElement
  console.log(textarea.value, textarea.selectionStart)
}

// HTMLFormElement for forms
function handleFormSubmit(event: SubmitEvent) {
  event.preventDefault()
  const form = event.target as HTMLFormElement
  const formData = new FormData(form)
}
</script>

Inline Event Handler Pattern

For simple cases, inline handlers with type annotations work well:

<template>
  <!-- Inline with type assertion -->
  <input
    @input="(event: Event) => {
      const input = event.target as HTMLInputElement
      searchQuery = input.value
    }"
  />

  <!-- Or with $event cast -->
  <input @input="searchQuery = ($event.target as HTMLInputElement).value" />
</template>

Generic Handler Pattern

Create reusable typed handlers:

// utils/events.ts
export function getInputValue(event: Event): string {
  return (event.target as HTMLInputElement).value
}

export function getSelectValue(event: Event): string {
  return (event.target as HTMLSelectElement).value
}

export function getCheckboxChecked(event: Event): boolean {
  return (event.target as HTMLInputElement).checked
}
<script setup lang="ts">
import { getInputValue, getCheckboxChecked } from '@/utils/events'

const name = ref('')
const agreed = ref(false)
</script>

<template>
  <input @input="e => name = getInputValue(e)" />
  <input type="checkbox" @change="e => agreed = getCheckboxChecked(e)" />
</template>

Vue Component Events

For Vue component events (not DOM events), use defineEmits for type safety:

<script setup lang="ts">
const emit = defineEmits<{
  'custom-event': [data: { id: number; name: string }]
}>()

// Handler for child component event
function handleChildEvent(data: { id: number; name: string }) {
  console.log(data.id, data.name)
}
</script>

<template>
  <!-- Custom component event - properly typed -->
  <ChildComponent @custom-event="handleChildEvent" />
</template>

Avoiding currentTarget vs target Confusion

function handleClick(event: MouseEvent) {
  // target: The element that triggered the event (could be a child)
  const target = event.target as HTMLElement

  // currentTarget: The element the listener is attached to
  const currentTarget = event.currentTarget as HTMLButtonElement

  // Be explicit about which you need
  if (target.tagName === 'SPAN') {
    // Clicked on span inside button
  }
}

Reference

Source: SKILL.md on GitHub

No alerts17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill is a comprehensive collection of educational debugging guides and best practices for Vue 3 developers. It contains no executable code or malicious instructions and focuses solely on diagnosing common runtime errors and performance issues.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    140 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at a5fc891. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 8 months ago

README badge

README badge for hyf0/vue-skills/vue-debug-guides

Guides for diagnosing and fixing Vue 3 runtime errors, reactivity bugs, async failures, and hydration issues across components, templates, watchers, forms, and lifecycle hooks. Covers specific failure modes like stale watchers, ref unwrapping edge cases, v-model sync problems, and SSR mismatches with concrete reference links for each issue.

Generated from the current SKILL.md.

Does this skill cover Vue 2?
No. This skill is specific to Vue 3 debugging and error handling.
What should I use for Vue development best practices instead of debugging?
The skill references a separate `vue-best-practices` skill for development best practices and common gotchas.
Does this cover SSR and hydration issues?
Yes. The skill includes guides for diagnosing and fixing SSR rendering differences and hydration bugs.
Can this help with async watcher and watchEffect problems?
Yes. The skill covers async operation stale data, dependency tracking after await, and proper flush timing for watchers.
Does this address v-model and form-related issues?
Yes. The skill includes guides for v-model initial values, textarea interpolation, IME composition, and custom checkbox form submission.

Generated from the current SKILL.md. These answers refresh after source changes.