I'm struggling to understand how one is supposed to work with custom events in Vue 3 with TSX templates.
Consider a simple parent/child app where child sends a custom event to parent:
const Child = defineComponent({
emits: {
boom: (x: number) => true,
},
setup(props, { emit }) {
console.log("Child props", props)
return () => (
<div onClick={() => emit("boom", 123)}>Click me!</div>
)
},
})
const Parent = defineComponent(() => {
return () => <Child onBoom={(x) => console.log(`Boom: ${x}`)} />
})
The above functions properly, and:
- In child
setup
,props
is empty (as I would expect). emit
call in child is properly typed (it requires a number to be passed as argument).
Unfortunately, it doesn't know how to type onBoom
in parent:
Property 'onBoom' does not exist on type 'IntrinsicAttributes & Partial<{}> & Pick<Readonly<{} & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, "style" | ... 8 more ... | "class">'.ts(2322)
Now if I add onBoom
property definition to Child:
const Child = defineComponent({
props: {
onBoom: Function as PropType<(x: string) => void>,
},
emits: {
boom: (x: number) => true,
},
...
Then it still functions properly, and onBoom
in parent becomes typed, but:
- Developer must repeat event definitions in two places.
- Nothing prevents these two definitions from going out of sync. In the example above, did you notice that I typed
x: string
instead ofx: number
inprops.onBoom
by mistake? - In child
setup
,props
is now not empty, it is an object withonBoom
property.
My question is: what is the "correct" way to handle custom events with Vue 3, Composition API and TSX templates?
Honestly, I am tempted to disregard the whole emits
/emit
machinery and simply use React-style callbacks with:
<div onClick={() => props.onBoom?.("five")}>
(What stops me at the moment is that I know that it may break when I decide to go with SSR, as these properties won't hydrate.)