Vue3中defineProps(定义onSave)与defineEmits(定义save)造成命名冲突
组件部分代码:
src\components\common\WorkflowMainDialog.vue
interface Props {
/** 对话框标题 */
title?: string;
/** 活动实例(包含必要信息) */
activityInstance: ActivityInstance;
/** 校验数据(返回 false 或 reject 则return) */
validate?: () => boolean | Promise<boolean>;
/** 保存函数(接收一个 done 回调,保存完成后调用) */
onSave?: (done: () => void) => void | Promise<void>;
}
const props = withDefaults(defineProps<Props>(), {
title: "流程办理",
validate: undefined,
onSave: undefined
});
const emit = defineEmits<{
/**
* 已办理(办理/退回后)
*/
audited: [];
/**
* 保存(已通过 onSave 处理,此事件可作为额外通知)
*/
save: [];
}>();
defineProps定义了onSave,defineEmits定义了save,造成命名冲突
使用组件
<WorkflowMainDialog
v-if="store.currentCapitalAllocate?.activityInstance"
ref="capitalAllocateWorkflowInstanceDialogRef"
title="资金分配"
:activity-instance="store.currentCapitalAllocate?.activityInstance"
:validate="validateAllocate"
:onSave="handleSaveAllocate"
@audited="handleAudited">
报错如下:

不能将类型“(done: () => void) => Promise<void>”分配给类型“((done: () => void) => void | Promise<void>) & (() => any)”。
不能将类型“(done: () => void) => Promise<void>”分配给类型“() => any”。
目标签名提供的自变量太少。预期为 1 个或更多,但实际为 0 个。ts-plugin(2322)
WorkflowMainDialog.vue(40, 3): 所需类型来自属性 "onSave",在此处的 "{ readonly title?: string | undefined; readonly activityInstance: ActivityInstance; readonly loading?: boolean | undefined; readonly isModified?: boolean | undefined; ... 5 more ...; readonly onAudited?: (() => any) | undefined; } & VNodeProps & AllowedComponentProps & ComponentCustomProps & Record<...>" 类型上声明该属性
通义灵码一键修复
(property) onSave?: (((done: () => void) => void | Promise<void>) & (() => any)) | undefined
保存函数(接收一个 done 回调,保存完成后调用)
您遇到的类型错误确实是由于 defineEmits 中声明了 save 事件,导致 Vue 自动生成一个同名的 onSave prop(类型为 () => any),与您在 defineProps 中自定义的 onSave(类型 (done: () => void) => void | Promise<void>)合并为交叉类型,无法兼容。
即使父组件没有使用 @save,类型系统仍然会认为组件可能接收这个 prop,因此冲突依然存在。
解决方案
1. 修改自定义 prop 名称(推荐)
将 onSave 改为不与事件监听器冲突的名称,例如 saveHandler。
修改组件内部:
ts
interface Props {
// ...
/** 保存函数(接收一个 done 回调,保存完成后调用) */
saveHandler?: (done: () => void) => void | Promise<void>; // 改名
}
// 使用处
const buttonSaveDisabled = computed(
() => props.loading || auditLoading.value || props.saving || !props.isModified || !props.saveHandler
);
const handleSaveWithDone = async (event?: MouseEvent, done?: () => void) => {
if (props.saveHandler) {
await props.saveHandler(() => {
done?.();
emit("save"); // 可选,仍可触发 save 事件
});
} else {
done?.();
}
};
模板中:
vue
<BasePreventReClickButtonEmit v-if="saveHandler" class="short-btn" type="primary" plain :disabled="buttonSaveDisabled" :timeout="0" @click="handleSaveWithDone" > 保存 </BasePreventReClickButtonEmit>
父组件使用时:
vue
<WorkflowMainDialog ... :save-handler="handleSaveAllocate" @audited="handleAudited" @save="onSaveComplete" <!-- 可选 --> >
2. 如果不需要 save 事件,可以从 defineEmits 中移除
ts
const emit = defineEmits<{
audited: [];
// 移除 save 事件
}>();
但保留事件有时是有用的,比如父组件想监听保存完成做额外操作。因此方案 1 更灵活。
为什么必须改名?
因为 Vue 的 <script setup> 会自动为 defineEmits 中声明的事件生成对应的 props(命名规则 onXxx),类型为 ((...args: any[]) => any) | undefined。这些 props 会与您在 defineProps 中声明的同名的 prop 合并,导致 TypeScript 要求您的 prop 同时满足两种签名。
改名后,自定义 prop saveHandler 与自动生成的 onSave 不再冲突,问题解决。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)