组件 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]