For generic components, having two emits with different args as "v" causes the callback to infer "unknown"
Consider this generic input component:
<template>
<input
ref="input"
:value="modelValue"
@input="(e) => $emit('update:modelValue', (e.target as HTMLInputElement).value)"
@keydown="e => e.code === 'Enter' && $emit('submit')"
/>
</template>
<script setup lang="ts" generic="T">
defineProps<{
modelValue: T,
}>()
defineEmits<{
(e: 'update:modelValue', v: T): void
(e: "submit", v: void): void
}>()
</script>
If you use it in a template like this:
<template>
<div>
<GenericComponent
:modelValue="val ?? ''"
@update:modelValue="v => val = v"
/>
</div>
</template>
The v value in the @update:modelValue callback will be of type unknown.
Rewriting the (e: "submit", v: void): void as (e: "submit"): void makes the language tools infer the generic type correctly.
I understand that in this specific case the v: void is unnecessary, but I just wanted to give a heads up in case it's an easy fix, otherwise you can close this issue.
Not sure if we should fix this issue :(
I understand how it's an issue with typescript overloading. I'm unsure if it worked before. but feel free to close this issue. I'm content so long as people like me can have something they can find it they encounter this issue, and know how to figure out workarounds.
Type conversion breaks with __VLS_NormalizeEmits, not sure if fix is possible.
<script setup lang="ts">
const events!: __VLS_NormalizeEmits<{
(e: "update:modelValue", v: number): void;
(e: "submit", v: void): void;
}>;
events;
// ^? { [x: string]: (...args: unknown[]) => void; }
</script>
An alternative is to define events via defineProps.
<script setup lang="ts" generic="T">
defineProps<{
modelValue: T,
'onUpdate:modelValue'(v: T): void,
}>()
defineEmits<{
(e: "submit", v: void): void
}>()
</script>
Mainly because __VLS_ConstructorOverloads cannot handle void type as arguments.
The problem is still existing now with the ultimate cause ↓
Using named tuple syntax can alleviate it.
I think I found a similar issue now arising when using the emitName: [emittype] syntax.
This playground example seems to work in the playgroung, but the typecheck via vue-tsc on my machine fails with the error
error TS2322: Type '(number: bigint, isDivisibleByZero: boolean) => void' is not assignable to type '(...args: unknown[]) => any'.
Types of parameters 'number' and 'args' are incompatible.
Type 'unknown' is not assignable to type 'bigint'.
14 <Child v-model="someNumber" @update:modelValue="logNumberAndIsDivisibleByZero"></Child>
In the playground example I assume the update:modelValue from defineModel and defined in defineEmits:
const emit = defineEmits<{
"update:modelValue": [number: bigint, isDivisibleByZero: boolean]
}>()
collide. The same does not happen when defining the event via:
const emit = defineEmits<{
(e: 'update:modelValue', number: bigint, isDivisibleByZero: boolean): void
}>()
(I still don't quite understand where the (...args: unknown[]) => any is coming from)
@ingoaf the type of update:modelValue event will be automatically generated, it will be intersected with the return type of defineEmits, then breaks.
@KazariEX thank you!
Do you know how the generated type looks like, so the intersection ends up being (...args: unknown[]) => any ?
@KazariEX thank you!
Do you know how the generated type looks like, so the intersection ends up being
(...args: unknown[]) => any?
import { defineComponent } from "vue";
type __VLS_Emit = {
"update:modelValue": [foo: string];
};
type __VLS_ModelEmit = {
"update:modelValue": [foo: string];
};
const Comp = defineComponent({
__typeEmits: {} as __VLS_Emit & __VLS_ModelEmit
});
new Comp().$emit;