# 機能設計書 14-defineProps() / defineEmits()

## 概要

本ドキュメントは、SFC `<script setup>` 内でProps・Emitsを型安全に定義するコンパイラマクロ `defineProps()` と `defineEmits()` の機能設計書である。

### 本機能の処理概要

`defineProps()` と `defineEmits()` は、Vue SFC（Single File Component）の `<script setup>` 構文内でコンポーネントのPropsとEmitsを宣言するためのコンパイラマクロである。これらはコンパイル時にコードが変換されるため、ランタイムでは実質的にno-op（警告のみ）となる。ランタイム宣言（配列・オブジェクト形式）と型ベース宣言（TypeScript型パラメータ）の2つの方法に対応し、型ベース宣言の場合はコンパイラが自動的にランタイムコードを生成する。

**業務上の目的・背景**：コンポーネントのインターフェース（Props/Emits）を型安全かつ簡潔に定義し、TypeScriptの型推論と連携した開発体験を提供する。`<script setup>` 構文の中核機能として、ボイラープレートコードの削減と型安全性の両立を実現する。

**機能の利用シーン**：全てのVue SFCコンポーネントで `<script setup>` を使用する際にProps・Emitsを定義する場面で使用される。

**主要な処理内容**：
1. **defineProps()のコンパイル時処理**：
   - ランタイム宣言の場合：引数をそのままPropsオプションとして出力
   - 型ベース宣言の場合：型パラメータからランタイムPropTypeデータを推論・生成
   - Propsデストラクチャリング対応（リアクティブデストラクチャ）
   - バインディングメタデータへの登録（`BindingTypes.PROPS`）
2. **defineEmits()のコンパイル時処理**：
   - ランタイム宣言の場合：引数をそのままEmitsオプションとして出力
   - 型ベース宣言の場合：型パラメータからイベント名を抽出してEmitsオプションを生成
   - defineModel()との連携（`update:` イベントの自動追加）

**関連システム・外部連携**：`@vue/compiler-sfc` パッケージでコンパイル時の変換が行われる。ランタイムの型定義は `@vue/runtime-core` パッケージに存在する。

**権限による制御**：権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Header（ナビゲーションバー） | 主機能 | store/prod/ssr/autoSave/themeのProps定義、toggle-theme等5種のカスタムイベント定義 |
| 3 | VersionSelect（バージョン選択） | 主機能 | pkg（パッケージ名）、label（表示ラベル）のProps定義 |

## 機能種別

コンパイラマクロ / 型定義ヘルパー

## 入力仕様

### defineProps() の入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| props（オーバーロード1） | PropNames[] | No | 文字列配列によるProp名宣言 | - |
| props（オーバーロード2） | ComponentObjectPropsOptions | No | オブジェクト形式のProp定義 | - |
| TypeProps（オーバーロード3） | 型パラメータ | No | TypeScript型による宣言 | 型パラメータとランタイム引数は同時に指定不可 |

### defineEmits() の入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| emitOptions（オーバーロード1） | EE[] | No | 文字列配列によるイベント名宣言 | - |
| emitOptions（オーバーロード2） | EmitsOptions | No | オブジェクト形式のイベント定義 | - |
| T（オーバーロード3） | 型パラメータ | No | TypeScript型による宣言 | 型パラメータとランタイム引数は同時に指定不可 |

### 入力データソース

`<script setup>` ブロック内で開発者が直接呼び出す（コンパイラマクロ）。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| defineProps戻り値 | Readonly\<ExtractPropTypes\<PP\>\> / DefineProps\<T, BKeys\> | 読み取り専用のPropsオブジェクト |
| defineEmits戻り値 | EmitFn\<E\> / ShortEmits\<T\> | 型付きemit関数 |

### 出力先

コンパイル後のコンポーネントオプションの `props` / `emits` フィールドに展開される。

## 処理フロー

### defineProps() の処理シーケンス（コンパイル時）

```
1. processDefineProps(ctx, node, declId) の呼び出し
   ├─ 重複チ�呼び出しチェック
   ├─ ランタイム引数の処理
   │   └─ 引数をctx.propsRuntimeDeclに設定、バインディングメタデータ登録
   ├─ 型パラメータの処理
   │   └─ ctx.propsTypeDeclに設定
   └─ Propsデストラクチャリングの処理
       └─ processPropsDestructure(ctx, declId)
2. genRuntimeProps(ctx) でランタイムコード生成
   ├─ ランタイム宣言の場合：そのまま出力（+ mergeDefaults）
   └─ 型宣言の場合：extractRuntimeProps(ctx) で型からランタイムコード生成
```

### defineEmits() の処理シーケンス（コンパイル時）

```
1. processDefineEmits(ctx, node, declId) の呼び出し
   ├─ 重複呼び出しチェック
   ├─ ランタイム引数の処理 → ctx.emitsRuntimeDecl
   └─ 型パラメータの処理 → ctx.emitsTypeDecl
2. genRuntimeEmits(ctx) でランタイムコード生成
   ├─ ランタイム宣言の場合：そのまま出力
   ├─ 型宣言の場合：extractRuntimeEmits(ctx) で型からイベント名抽出
   └─ defineModel連携：mergeModelsでupdate:イベント追加
```

### フローチャート

```mermaid
flowchart TD
    A[script setup解析] --> B{defineProps呼び出し検出?}
    B -->|Yes| C{重複チェック}
    C -->|重複| D[エラー]
    C -->|OK| E{型パラメータあり?}
    E -->|Yes| F[propsTypeDecl設定]
    E -->|No| G[propsRuntimeDecl設定]
    F --> H{ランタイム引数もあり?}
    H -->|Yes| I[エラー: 同時指定不可]
    H -->|No| J[genRuntimeProps: 型から推論]
    G --> K[genRuntimeProps: そのまま出力]
    J --> L[コンパイル出力]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-14-01 | 単一呼び出し制限 | defineProps()、defineEmits()はそれぞれ1回のみ呼び出し可能 | コンパイル時 |
| BR-14-02 | 型とランタイムの排他 | 型パラメータとランタイム引数は同時に指定できない | コンパイル時 |
| BR-14-03 | ランタイム警告 | 実行時に呼ばれた場合は警告を出してnullを返す | DEVモードのランタイム |
| BR-14-04 | バインディング登録 | 定義されたProp名はBindingTypes.PROPSとしてバインディングメタデータに登録 | コンパイル時 |
| BR-14-05 | defineModel連携 | defineEmitsの出力にdefineModelの`update:`イベントが自動追加される | defineModelが使用されている場合 |

### 計算ロジック

型ベースのdefinePropsでは、TypeScript型からランタイム型情報（type, required, skipCheck）を推論する `extractRuntimeProps` / `resolveRuntimePropsFromType` 関数が中心的なロジックである。

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | コンパイルエラー | defineProps()が2回以上呼ばれた | 1回のみに修正 |
| - | コンパイルエラー | 型パラメータとランタイム引数の同時指定 | どちらか一方に統一 |
| - | コンパイルエラー | defineEmits()が2回以上呼ばれた | 1回のみに修正 |
| - | ランタイム警告 | `<script setup>` 外での呼び出し | `<script setup>` 内で使用する |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- コンパイル時に型からランタイムコードが生成されるため、実行時の型チェックオーバーヘッドはない。
- プロダクションビルドでは `isProd` フラグにより不要な型情報（type, required, skipCheck）が省略される（Boolean/Functionを除く）。

## セキュリティ考慮事項

特になし。

## 備考

- `defineProps()` はコンパイラマクロであり、importなしで使用可能。
- Propsのリアクティブデストラクチャリング（`const { foo } = defineProps(...)`）はVue 3.5以降でサポートされている。

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: ランタイム型定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | apiSetupHelpers.ts | `packages/runtime-core/src/apiSetupHelpers.ts` | defineProps関数のランタイムスタブ（76-94行目）、defineEmits関数のランタイムスタブ（135-152行目） |
| 1-2 | apiSetupHelpers.ts | `packages/runtime-core/src/apiSetupHelpers.ts` | DefineProps型（96-98行目）、ComponentTypeEmits型（154行目）、ShortEmits型（158-162行目） |

**読解のコツ**: ランタイム実装は `warnRuntimeUsage()` で警告を出して `null as any` を返すだけ。実質的な処理はコンパイラ側で行われる。

#### Step 2: コンパイラ側のdefineProps処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | defineProps.ts | `packages/compiler-sfc/src/script/defineProps.ts` | processDefineProps関数（47-93行目） |
| 2-2 | defineProps.ts | `packages/compiler-sfc/src/script/defineProps.ts` | processWithDefaults関数（95-139行目） |
| 2-3 | defineProps.ts | `packages/compiler-sfc/src/script/defineProps.ts` | genRuntimeProps関数（141-177行目） |
| 2-4 | defineProps.ts | `packages/compiler-sfc/src/script/defineProps.ts` | extractRuntimeProps関数（179-209行目）、resolveRuntimePropsFromType関数（211-238行目） |

**主要処理フロー**:
1. **53-55行目**: isCallOf(node, DEFINE_PROPS) でdefineProps呼び出しを検出
2. **57-59行目**: 重複呼び出しチェック
3. **61行目**: ランタイム引数を ctx.propsRuntimeDecl に設定
4. **73-82行目**: 型パラメータがある場合は ctx.propsTypeDecl に設定
5. **141-177行目**: genRuntimePropsでランタイムコード生成（型推論 or そのまま出力）

#### Step 3: コンパイラ側のdefineEmits処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | defineEmits.ts | `packages/compiler-sfc/src/script/defineEmits.ts` | processDefineEmits関数（19-46行目） |
| 3-2 | defineEmits.ts | `packages/compiler-sfc/src/script/defineEmits.ts` | genRuntimeEmits関数（48-71行目） |
| 3-3 | defineEmits.ts | `packages/compiler-sfc/src/script/defineEmits.ts` | extractRuntimeEmits関数（73-103行目） |

**主要処理フロー**:
1. **24-26行目**: isCallOf(node, DEFINE_EMITS) で検出
2. **27-29行目**: 重複呼び出しチェック
3. **48-71行目**: genRuntimeEmitsでランタイムコード生成、defineModelのupdate:イベントとのmerge

### プログラム呼び出し階層図

```
compileScript(sfc, options)
    │
    ├─ processDefineProps(ctx, node, declId)
    │      ├─ getObjectOrArrayExpressionKeys() - バインディング登録
    │      ├─ processPropsDestructure() - デストラクチャ処理
    │      └─ processWithDefaults() - withDefaultsとの連携
    │
    ├─ processDefineEmits(ctx, node, declId)
    │
    ├─ genRuntimeProps(ctx)
    │      ├─ extractRuntimeProps(ctx)
    │      │      └─ resolveRuntimePropsFromType(ctx, node)
    │      └─ genModelProps(ctx) - defineModel連携
    │
    └─ genRuntimeEmits(ctx)
           └─ extractRuntimeEmits(ctx)
                  └─ extractEventNames(ctx, eventName, emits)
```

### データフロー図

```
[入力]                            [処理]                        [出力]

defineProps({ foo: String })  ▶ processDefineProps()       ▶ props: { foo: String }
defineProps<{ foo: string }>  ▶ extractRuntimeProps()      ▶ props: { foo: { type: String, required: true } }
defineEmits(['change'])       ▶ processDefineEmits()       ▶ emits: ['change']
defineEmits<{ change: [] }>  ▶ extractRuntimeEmits()      ▶ emits: ['change']
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apiSetupHelpers.ts | `packages/runtime-core/src/apiSetupHelpers.ts` | ソース | defineProps/defineEmitsのランタイムスタブ |
| defineProps.ts | `packages/compiler-sfc/src/script/defineProps.ts` | ソース | definePropsのコンパイラ処理 |
| defineEmits.ts | `packages/compiler-sfc/src/script/defineEmits.ts` | ソース | defineEmitsのコンパイラ処理 |
| definePropsDestructure.ts | `packages/compiler-sfc/src/script/definePropsDestructure.ts` | ソース | Propsデストラクチャリング処理 |
| context.ts | `packages/compiler-sfc/src/script/context.ts` | ソース | ScriptCompileContext（コンパイルコンテキスト） |
| resolveType.ts | `packages/compiler-sfc/src/script/resolveType.ts` | ソース | 型解決・型推論 |
| compileScript.ts | `packages/compiler-sfc/src/compileScript.ts` | ソース | SFCスクリプトコンパイルのエントリーポイント |
| scriptSetupHelpers.d.ts | `packages/runtime-core/types/scriptSetupHelpers.d.ts` | 型定義 | グローバル型定義 |
