`defineComponent` prop types are broken in "Functional Signature" mode
Vue version
3.3.8
Link to minimal reproduction
Steps to reproduce
I have basically taken an example from the docs and simplified it a bit:
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent(
<T>(_: { msg: T; list: T[] }) => () => null,
{ props: ["msg", "list"] },
)
</script>
What is expected?
I expected the type of this component after import to be the following:
import Comp from "./Comp.vue";
type Temp = typeof Comp;
// ^? type Temp = <T>(props: { msg: T; list: [T] } & {}) => any
What is actually happening?
any
is replacing the T
parameters:
import Comp from "./Comp.vue";
type Temp = typeof Comp;
// ^? type Temp = (props: { msg: any; list: any } & {}) => any
System Info
No response
Any additional comments?
A workaround is casting props
to never[]
:
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent(
<T>(_: { msg: T; list: T[] }) => () => null,
{ props: ["msg", "list"] as never[] },
)
</script>
Then typeOf Comp
returns expected type.
Again, how do you set EmitsOptions
in Functional Signature
mode ?
import type { SetupContext } from 'vue';
const Comp = defineComponent(
<T extends string | number>(
props: { modelValue: T },
ctx: SetupContext<{ 'update:modelValue': (val: T) => void }>
) => {
return () => {
return <div>{props.modelValue}</div>;
};
},
// manual runtime props declaration is currently still needed.
{
props: ['modelValue'] as never,
emits: ['update:modelValue'],
}
);
expected
const Comp: <T extends string | number>(props: {
modelValue: T;
} & {
"onUpdate:modelValue"?: ((val: T) => void) | undefined;
}) => any
now
const Comp: <T extends string | number>(props: {
modelValue: T;
} & {
"onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
}) => any
@wangcch Here's what I came up with:
<script setup lang="ts">
import { defineComponent, h } from 'vue';
const Comp = defineComponent(
<T extends string | number>(props: {
modelValue: T;
"onUpdate:modelValue"?: (val: T) => void;
}) =>
() => h("div", props.modelValue),
{ props: ["modelValue", "onUpdate:modelValue"] as never },
);
// const Comp: <T extends string | number>(props: {
// modelValue: T;
// "onUpdate:modelValue"?: (val: T) => void;
// } & {}) => any
// You have to narrow down the type of your component beforehand, because of
// https://github.com/vuejs/language-tools/issues/3745
const CompString = Comp<string>;
</script>
<template>
<CompString model-value="123" @update:model-value="(x) => x" />
</template>
@rijenkii Thank you very much ~
Cool~ This is indeed a way. v-model
also works.
-- { props: ["modelValue", "ononUpdate:modelValue"] as never },
++ { props: ["modelValue", "onUpdate:modelValue"] as never },