返回列表 发布新帖
查看: 75|回复: 0

组件 Props 和 Emits 的类型声明

发表于 2025-9-28 17:00:58 | 查看全部 |阅读模式

这里或许是互联网从业者的最后一片净土,随客社区期待您的加入!

您需要 登录 才可以下载或查看,没有账号?立即注册

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

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

  2. interface User {
  3.   id: number
  4.   name: string
  5.   email: string
  6. }

  7. export default defineComponent({
  8.   props: {
  9.     // 基础类型
  10.     title: String,
  11.     count: {
  12.       type: Number,
  13.       required: true
  14.     },
  15.    
  16.     // 对象类型
  17.     user: {
  18.       type: Object as PropType<User>,
  19.       required: true
  20.     },
  21.    
  22.     // 数组类型
  23.     tags: {
  24.       type: Array as PropType<string[]>,
  25.       default: () => []
  26.     },
  27.    
  28.     // 联合类型
  29.     status: {
  30.       type: String as PropType<'pending' | 'success' | 'error'>,
  31.       default: 'pending'
  32.     },
  33.    
  34.     // 函数类型
  35.     onSubmit: {
  36.       type: Function as PropType<(data: any) => Promise<void>>,
  37.       required: false
  38.     }
  39.   }
  40. })
复制代码
  组合式API < script setup >
  1. <script setup lang="ts">
  2. // 方式1: 使用接口
  3. interface User {
  4.   id: number
  5.   name: string
  6.   email: string
  7. }

  8. interface Props {
  9.   title?: string
  10.   count: number
  11.   user: User
  12.   tags?: string[]
  13.   status?: 'pending' | 'success' | 'error'
  14.   onSubmit?: (data: any) => Promise<void>
  15. }

  16. const props = defineProps<Props>()

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

  26. // 方式3: 运行时声明 + 类型(支持默认值)
  27. const props = withDefaults(defineProps<{
  28.   title?: string
  29.   count: number
  30.   user: User
  31.   tags?: string[]
  32.   status?: 'pending' | 'success' | 'error'
  33. }>(), {
  34.   title: '默认标题',
  35.   tags: () => ['vue', 'typescript'],
  36.   status: 'pending'
  37. })
  38. </script>
复制代码
2. Emits 的类型声明
  选项式API
  1. import { defineComponent } from 'vue'

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

  9. // 方式2: 更简洁的语法(Vue 3.3+)
  10. const emit = defineEmits<{
  11. click: []
  12. 'update:modelValue': [value: string]
  13. submit: [payload: { email: string; password: string }]
  14. error: [message: string, code?: number]
  15. }>()

  16. // 使用示例
  17. const handleClick = () => {
  18. emit('click')
  19. }

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

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

  26. const handleError = () => {
  27. emit('error', 'Something went wrong', 500)
  28. }
  29. </script>
复制代码
3.完整的组件示例
  1. <script setup lang="ts">
  2. // 类型定义
  3. interface User {
  4.   id: number
  5.   name: string
  6.   email: string
  7.   avatar?: string
  8. }

  9. interface FormData {
  10.   title: string
  11.   content: string
  12.   tags: string[]
  13. }

  14. // Props 类型
  15. interface Props {
  16.   user: User
  17.   initialData?: FormData
  18.   loading?: boolean
  19.   disabled?: boolean
  20.   maxLength?: number
  21. }

  22. const props = withDefaults(defineProps<Props>(), {
  23.   loading: false,
  24.   disabled: false,
  25.   maxLength: 1000
  26. })

  27. // Emits 类型
  28. const emit = defineEmits<{
  29.   (e: 'submit', data: FormData): void
  30.   (e: 'cancel'): void
  31.   (e: 'update:user', user: Partial<User>): void
  32.   (e: 'error', message: string): void
  33. }>()

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

  40. const handleSubmit = () => {
  41.   if (formData.title.trim() === '') {
  42.     emit('error', '标题不能为空')
  43.     return
  44.   }
  45.   
  46.   emit('submit', { ...formData })
  47. }

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

  52. <template>
  53.   <form @submit.prevent="handleSubmit" class="user-form">
  54.     <div class="form-group">
  55.       <label>标题</label>
  56.       <input
  57.         v-model="formData.title"
  58.         :disabled="props.disabled || props.loading"
  59.         :maxlength="props.maxLength"
  60.       />
  61.     </div>
  62.    
  63.     <div class="form-group">
  64.       <label>内容</label>
  65.       <textarea
  66.         v-model="formData.content"
  67.         :disabled="props.disabled || props.loading"
  68.       />
  69.     </div>
  70.    
  71.     <div class="actions">
  72.       <button
  73.         type="button"
  74.         @click="$emit('cancel')"
  75.         :disabled="props.loading"
  76.       >
  77.         取消
  78.       </button>
  79.       <button
  80.         type="submit"
  81.         :disabled="props.disabled || props.loading"
  82.       >
  83.         {{ props.loading ? '提交中...' : '提交' }}
  84.       </button>
  85.     </div>
  86.   </form>
  87. </template>
复制代码

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2001-2025 Suike Tech All Rights Reserved. 随客交流社区 (备案号:津ICP备19010126号) |Processed in 0.118463 second(s), 7 queries , Gzip On, MemCached On.
关灯 在本版发帖返回顶部
快速回复 返回顶部 返回列表