Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.8 KiB
5.8 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||
|---|---|---|---|---|---|---|---|---|---|
| Always Explicitly Type Event Handlers | MEDIUM | Without explicit typing, event parameters have implicit 'any' type causing TypeScript errors in strict mode | gotcha |
|
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
eventparameter 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
}
}