We use cookies to enhance your experience on the site
CodeWorlds

Typing Emits

Just as props define the input data of a component, emits define its output events. With TypeScript, we can precisely type what events a component emits and what data each event carries.

defineEmits with Generic Type

1<script setup lang="ts">
2// Generic syntax
3const emit = defineEmits<{
4  (e: 'update', value: string): void
5  (e: 'delete', id: number): void
6  (e: 'close'): void
7}>()
8
9// Usage
10emit('update', 'new value')  // OK
11emit('delete', 42)           // OK
12emit('close')                // OK
13// emit('update', 123)       // ERROR! Expected string, not number
14// emit('unknown')           // ERROR! Unknown event
15</script>

New Syntax 3.3+

Since Vue 3.3, a shorter syntax is available:

1<script setup lang="ts">
2const emit = defineEmits<{
3  update: [value: string]
4  delete: [id: number]
5  close: []
6}>()
7</script>

Both syntaxes are equivalent. The new one is more concise — we use the event name as the key and arguments as a tuple.

Typed Events in Practice

Here's an example of a station control panel:

1<script setup lang="ts">
2interface ModuleEvent {
3  moduleId: string
4  timestamp: number
5}
6
7const emit = defineEmits<{
8  activate: [event: ModuleEvent]
9  deactivate: [event: ModuleEvent]
10  alert: [level: 'low' | 'medium' | 'high', message: string]
11}>()
12
13function handleActivation(moduleId: string) {
14  emit('activate', {
15    moduleId,
16    timestamp: Date.now()
17  })
18}
19
20function triggerAlert() {
21  emit('alert', 'high', 'Quantum anomaly detected!')
22}
23</script>

Parent-Child Communication

The child component emits events, the parent listens:

1<!-- ControlPanel.vue (child) -->
2<script setup lang="ts">
3const emit = defineEmits<{
4  command: [action: string, target: string]
5}>()
6</script>
7
8<template>
9  <button @click="emit('command', 'scan', 'sector-7')">
10    Scan Sector 7
11  </button>
12</template>
1<!-- App.vue (parent) -->
2<template>
3  <ControlPanel @command="handleCommand" />
4</template>
5
6<script setup lang="ts">
7function handleCommand(action: string, target: string) {
8  console.log(action, target)
9  // TypeScript knows the argument types!
10}
11</script>

Combining Props and Emits

The v-model pattern with types:

1<script setup lang="ts">
2const props = defineProps<{
3  modelValue: string
4}>()
5
6const emit = defineEmits<{
7  'update:modelValue': [value: string]
8}>()
9
10function onInput(event: Event) {
11  const target = event.target as HTMLInputElement
12  emit('update:modelValue', target.value)
13}
14</script>
15
16<template>
17  <input :value="modelValue" @input="onInput" />
18</template>
Go to CodeWorlds