zzz 发表于 2025-9-28 17:00:58

组件 Props 和 Emits 的类型声明

本帖最后由 zzz 于 2025-9-28 17:09 编辑

在 Vue 3 中,我们可以使用 TypeScript 为组件的 props 和 emits 提供类型声明。
1.Props的类型声明选项式APIimport { defineComponent, PropType } from 'vue'

interface User {
id: number
name: string
email: string
}

export default defineComponent({
props: {
    // 基础类型
    title: String,
    count: {
      type: Number,
      required: true
    },
   
    // 对象类型
    user: {
      type: Object as PropType<User>,
      required: true
    },
   
    // 数组类型
    tags: {
      type: Array as PropType<string[]>,
      default: () => []
    },
   
    // 联合类型
    status: {
      type: String as PropType<'pending' | 'success' | 'error'>,
      default: 'pending'
    },
   
    // 函数类型
    onSubmit: {
      type: Function as PropType<(data: any) => Promise<void>>,
      required: false
    }
}
})组合式API < script setup ><script setup lang="ts">
// 方式1: 使用接口
interface User {
id: number
name: string
email: string
}

interface Props {
title?: string
count: number
user: User
tags?: string[]
status?: 'pending' | 'success' | 'error'
onSubmit?: (data: any) => Promise<void>
}

const props = defineProps<Props>()

// 方式2: 内联类型声明
const props = defineProps<{
title?: string
count: number
user: User
tags?: string[]
status?: 'pending' | 'success' | 'error'
onSubmit?: (data: any) => Promise<void>
}>()

// 方式3: 运行时声明 + 类型(支持默认值)
const props = withDefaults(defineProps<{
title?: string
count: number
user: User
tags?: string[]
status?: 'pending' | 'success' | 'error'
}>(), {
title: '默认标题',
tags: () => ['vue', 'typescript'],
status: 'pending'
})
</script>2. Emits 的类型声明选项式APIimport { defineComponent } from 'vue'

export default defineComponent({
emits: {
    // 无参数事件
    'click': null,
   
    // 带参数事件(运行时验证)
    'update:modelValue': (value: string) => typeof value === 'string',
   
    // 复杂参数事件
    'submit': (payload: { email: string; password: string }) => {
      return payload.email !== undefined && payload.password !== undefined
    }
}
})组合式API < script setup ><script setup lang="ts">
// 方式1: 类型字面量(推荐)
const emit = defineEmits<{
(e: 'click'): void
(e: 'update:modelValue', value: string): void
(e: 'submit', payload: { email: string; password: string }): void
(e: 'error', message: string, code?: number): void
}>()

// 方式2: 更简洁的语法(Vue 3.3+)
const emit = defineEmits<{
click: []
'update:modelValue':
submit:
error:
}>()

// 使用示例
const handleClick = () => {
emit('click')
}

const handleInput = (value: string) => {
emit('update:modelValue', value)
}

const handleSubmit = () => {
emit('submit', { email: 'test@example.com', password: '123456' })
}

const handleError = () => {
emit('error', 'Something went wrong', 500)
}
</script>3.完整的组件示例<script setup lang="ts">
// 类型定义
interface User {
id: number
name: string
email: string
avatar?: string
}

interface FormData {
title: string
content: string
tags: string[]
}

// Props 类型
interface Props {
user: User
initialData?: FormData
loading?: boolean
disabled?: boolean
maxLength?: number
}

const props = withDefaults(defineProps<Props>(), {
loading: false,
disabled: false,
maxLength: 1000
})

// Emits 类型
const emit = defineEmits<{
(e: 'submit', data: FormData): void
(e: 'cancel'): void
(e: 'update:user', user: Partial<User>): void
(e: 'error', message: string): void
}>()

// 组件逻辑
const formData = reactive<FormData>({
title: props.initialData?.title || '',
content: props.initialData?.content || '',
tags: props.initialData?.tags || []
})

const handleSubmit = () => {
if (formData.title.trim() === '') {
    emit('error', '标题不能为空')
    return
}

emit('submit', { ...formData })
}

const handleUserUpdate = (updates: Partial<User>) => {
emit('update:user', updates)
}
</script>

<template>
<form @submit.prevent="handleSubmit" class="user-form">
    <div class="form-group">
      <label>标题</label>
      <input
      v-model="formData.title"
      :disabled="props.disabled || props.loading"
      :maxlength="props.maxLength"
      />
    </div>
   
    <div class="form-group">
      <label>内容</label>
      <textarea
      v-model="formData.content"
      :disabled="props.disabled || props.loading"
      />
    </div>
   
    <div class="actions">
      <button
      type="button"
      @click="$emit('cancel')"
      :disabled="props.loading"
      >
      取消
      </button>
      <button
      type="submit"
      :disabled="props.disabled || props.loading"
      >
      {{ props.loading ? '提交中...' : '提交' }}
      </button>
    </div>
</form>
</template>
页: [1]
查看完整版本: 组件 Props 和 Emits 的类型声明