Vue3动态组件TypeScript类型报错:如何正确定义类型?
Vue3动态组件的类型标注问题
组件代码示例
Child.vue
<script lang='ts' setup> // 省略部分代码 const emit = defineEmits<{ (e: 'formErrors', errors: ValidationErrorsType): void, (e: 'formSuccess', success: boolean): void }>() // 省略部分代码 const submit = () => { // 省略部分代码 emit('formErrors', errors); } </script> <!-- 省略其余代码 -->
Parent.vue
<script lang='ts' setup> const formErrors = ref<ValidationErrorsType>({}); const setFormErrors = (errors: ValidationErrorsType) => { formErrors.value = errors } </script> <template> <component :is='Child' @form-errors='setFormErrors'/> </template>
报错信息
[vue-tsc] Type '(errors: ValidationErrorsType) => void' is not assignable to type '(...args: unknown[]) => void'. Types of parameters 'errors' and 'args' are incompatible. Type 'unknown' is not assignable to type 'Record<string, string>'.
临时现象
保持setFormErrors的参数类型定义时,报错会消失:
const setFormErrors = (errors: ValidationErrorsType) => { formErrors.value = errors }
问题
能否为Vue3动态组件正确设置类型?
答案:可以,以下是几种可行方案
问题根源是Vue动态组件的默认类型为Component,其事件回调参数会被推断为unknown,导致类型不匹配。我们可以通过以下方式让TypeScript识别组件的具体事件类型:
方案1:为动态组件断言具体类型
在脚本中声明子组件的实例类型,再在模板中为动态组件做类型断言,让TS识别其事件定义:
<script lang='ts' setup> import Child from './Child.vue'; import type { ComponentPublicInstance } from 'vue'; // 声明Child组件的实例类型,包含其emits定义 type ChildInstance = ComponentPublicInstance<{}, {}, {}, {}, typeof Child['emits']>; const formErrors = ref<ValidationErrorsType>({}); const setFormErrors = (errors: ValidationErrorsType) => { formErrors.value = errors } </script> <template> <component :is='Child as ChildInstance' @form-errors='setFormErrors'/> </template>
方案2:直接在事件回调中指定参数类型
在模板绑定事件时,显式指定回调参数的类型,跳过TS的默认推断:
<template> <component :is='Child' @form-errors='(errors: ValidationErrorsType) => formErrors.value = errors'/> </template>
方案3:导出并复用子组件的Emits类型
在子组件中导出事件类型,父组件导入后用该类型约束回调函数:
// Child.vue <script lang='ts' setup> // 导出事件类型 export type ChildEmits = { (e: 'formErrors', errors: ValidationErrorsType): void, (e: 'formSuccess', success: boolean): void } const emit = defineEmits<ChildEmits>() // 省略其余代码 </script>
// Parent.vue <script lang='ts' setup> import Child from './Child.vue'; import type { ChildEmits } from './Child.vue'; const formErrors = ref<ValidationErrorsType>({}); // 用ChildEmits约束回调函数的参数类型 const setFormErrors: (errors: Parameters<ChildEmits[0]>[1]) => void = (errors) => { formErrors.value = errors } </script> <template> <component :is='Child' @form-errors='setFormErrors'/> </template>
以上方案均可解决动态组件的类型不匹配问题,让TS正确校验事件回调的参数类型。
内容的提问来源于stack exchange,提问作者Evgeny Anisimov
相关产品推荐
相关产品推荐

