Vue + TS — Emits 型付け
テクノロジ系 / L06 vue-typescript — TypeScript 連携
Vue + TS — Emits 型付け
Function 形式
<script setup lang="ts">
const emit = defineEmits<{
(e: "increment"): void;
(e: "set", value: number): void;
(e: "submit", payload: { name: string; age: number }): void;
}>();
function handleClick() {
emit("increment");
emit("set", 100);
emit("submit", { name: "Taro", age: 20 });
}
</script>(e: "name", ...args): void の形式で複数並べる。
Object 形式 (推奨・新しめ)
<script setup lang="ts">
const emit = defineEmits<{
increment: [];
set: [value: number];
submit: [payload: { name: string; age: number }];
}>();
</script>event名: [引数の型] で書ける。
Vue 3.3+ 推奨、読みやすい。
親側
<Counter @increment="handleIncrement" @set="(v) => count = v" @submit="(p) => save(p)" />
引数の型が自動推論される (TS の恩恵)。
defineModel (Vue 3.4+)
defineProps + defineEmits を 1行に圧縮:
<script setup lang="ts">
const text = defineModel<string>();
const isOpen = defineModel<boolean>("open", { default: false });
</script>
<template>
<input v-model="text" />
<Dialog v-model:open="isOpen" />
</template>ラッパー input / dialog / select で激減。
共有型
// types/events.ts
export type FormSubmitEvent = {
name: string;
age: number;
email: string;
};<script setup lang="ts">
import type { FormSubmitEvent } from "@/types/events.js";
const emit = defineEmits<{
submit: [event: FormSubmitEvent];
}>();
</script>複数 component で同じ event を扱うときに共有。
まとめ
- 型ベース
defineEmits<{...}>()で型安全 - Function 形式と Object 形式 (3.3+ 推奨)
- 親側の handler 引数が自動推論
- defineModel (3.4+) で v-model を簡潔に
- 共有型は別ファイル + import type
L06 完。次は L07 テスト。
考えてみよう
既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。
Lv1: 基本 — Object 形式 (3.3+)
問題
<!-- Counter.vue -->
<script setup lang="ts">
const emit = defineEmits<{
increment: [];
set: [value: number];
submit: [payload: { name: string; age: number }];
}>();
</script>
<template>
<button @click="emit('increment')">+</button>
<button @click="emit('set', 100)">100</button>
<button @click="emit('submit', { name: 'Taro', age: 20 })">Submit</button>
</template>
<!-- 親 -->
<Counter
@increment="handleInc"
@set="(v) => handleSet(v)"
@submit="(p) => handleSubmit(p)"
/>合格基準
- 3 emit が型付きで親に届く
- 引数も型推論
Lv2: 応用 — Function 形式と比較
問題
同じものを 2形式で書き、好みを論じる。
// Function 形式 (旧)
const emit = defineEmits<{
(e: "increment"): void;
(e: "set", value: number): void;
}>();
// Object 形式 (3.3+ 推奨)
const emit = defineEmits<{
increment: [];
set: [value: number];
}>();合格基準
- 両方動く
- Object 形式の利点 (短い・読みやすい) を理解
Lv3: 発展 — defineModel + 複数
問題
<!-- NameForm.vue -->
<script setup lang="ts">
const firstName = defineModel<string>("first", { default: "" });
const lastName = defineModel<string>("last", { default: "" });
</script>
<template>
<input v-model="firstName" placeholder="First" />
<input v-model="lastName" placeholder="Last" />
</template>
<!-- 親 -->
<NameForm v-model:first="fn" v-model:last="ln" />
<p>{{ fn }} {{ ln }}</p>合格基準
- 2 v-model が独立に動く
- defineModel で defineProps + defineEmits を集約
提出
submission.md に3問の回答を。