採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

Vue + TS — Props 型付け

テクノロジ系 / L06 vue-typescript — TypeScript 連携

Vue + TS — Props 型付け

基本パターン (再掲)

<script setup lang="ts">
const props = defineProps<{
  name: string;
  age: number;
  email?: string;
}>();
</script>

defineProps<{...}>() で TS の型から自動生成。

interface を別出し (推奨)

<script setup lang="ts">
interface Props {
  user: User;
  showActions?: boolean;
  onUpdate?: (user: User) => void;
}

const props = defineProps<Props>();
</script>

interface を上で定義 → 読みやすい・再利用しやすい。

外部 import

// types/user.ts
export interface User {
  id: number;
  name: string;
  email: string;
}
<script setup lang="ts">
import type { User } from "@/types/user.js";

interface Props {
  user: User;
}
const props = defineProps<Props>();
</script>

複数 component で共有する型は別ファイルへ。

import typeコンパイル後の JS から消える

withDefaults

<script setup lang="ts">
interface Props {
  count?: number;
  label?: string;
  items?: string[];
}

const props = withDefaults(defineProps<Props>(), {
  count: 0,
  label: "Item",
  items: () => [],          // 配列・objectは関数で返す
});
</script>

配列やオブジェクトは 関数で返す こと (毎インスタンスで新規)。

inline 型

<script setup lang="ts">
const props = defineProps<{
  onSubmit: (data: { name: string; age: number }) => Promise<void>;
}>();
</script>

その component でしか使わない型ならインライン。

共有するなら interface 別出し。

TS の型 + バリデーション

実行時バリデーションも必要なら Zod:

<script setup lang="ts">
import { z } from "zod";

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
});

interface Props {
  userJson: unknown;
}

const props = defineProps<Props>();
const user = computed(() => UserSchema.parse(props.userJson));
</script>

外部 (API) から来るデータの形を保証。

まとめ

  • defineProps<Props>() でTS型ベース
  • interface 別出しで読みやすく
  • withDefaults でデフォルト値、配列は関数で
  • 共有型は別ファイル + import type
  • 実行時検証は Zod 併用

最後は Emits の型付け

考えてみよう

既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。

Lv1: 基本 — defineProps<Props>

問題

<script setup lang="ts">
interface Props {
  user: { id: number; name: string };
  showActions?: boolean;
}

const props = defineProps<Props>();
</script>

<template>
  <div>
    <h2>{{ props.user.name }}</h2>
    <button v-if="props.showActions">Edit</button>
  </div>
</template>

合格基準

  • TS 推論で props 補完
  • 不正な値の代入で型エラー

Lv2: 応用 — 共有型を別ファイル

問題

// types/user.ts
export interface User { id: number; name: string; email: string }
<script setup lang="ts">
import type { User } from "@/types/user.js";

const props = defineProps<{ user: User }>();
</script>

複数 component で同じ User 型を共有。

合格基準

  • 型を別ファイルから import
  • import type で実行時に消える

Lv3: 発展 — withDefaults

問題

<script setup lang="ts">
interface Props {
  count?: number;
  label?: string;
  items?: string[];
}

const props = withDefaults(defineProps<Props>(), {
  count: 0,
  label: "Default",
  items: () => [],   // ← 配列は関数で返す
});
</script>

<template>
  <p>{{ props.label }} ({{ props.count }})</p>
  <ul><li v-for="i in props.items" :key="i">{{ i }}</li></ul>
</template>

合格基準

  • デフォルト値が効く
  • 配列が 関数で返す 理由 (毎インスタンスで新規) を理解

提出

submission.md に3問の回答を。