`defineComponent` : `composableFunction` references a type with a cyclic structure error
Vue version
3.3.8
Link to minimal reproduction
Steps to reproduce
I create two composable for creating component with defineComponent
.
composableA
: It creates component which will works as a child. I pass the types for the props
type Base<T = string> = {
name: T extends string ? string : never;
}
export const composableA = <K>() => {
const cmp = defineComponent<Base<keyof K>>({});
return cmp;
};
composableB
: This creates a parent and this composable will give back the child and parent together:
export const composableB = <T>() => {
const componentA = defineComponent({});
const componentB = composableA<T>();
return {
componentA,
componentB,
};
};
On code level this is working as expected. If I have type then then name prop will be type safe. But the lint is not so happy about it and I get this error:
The inferred type of 'composableB' references a type with a cyclic structure which cannot be trivially serialized. A type annotation is necessary.
I noticed the problem with the occured with the <Base<keyof K>>
. If I not using generic with Base
or just using the K
like this:
const cmp = defineComponent<K>({});
or
const cmp = defineComponent<keyof K>({});
the error is gone but when I pass it to the Base
like this:
const cmp = defineComponent<Base<keyof K>>({});
then it fails
(I'm not sure this is a Vue or a Typescript related issue or mine of course)
What is expected?
Not getting this error
What is actually happening?
Getting this error
System Info
No response
Any additional comments?
No response
This issue is caused mainly because of the generic
than the defineComponent
, even is not a valid type as for vue props, because the name: never
will not make the type valid at runtime, since name
will always be a prop of the component.
That said there's a change on the type from passed props to the defineComponent
declared props, you can mitigate that with declaring the prop and overriding it's runtime type.
example:
import { PropType, defineComponent } from "vue";
export const composableA = <K>() => {
const cmp = defineComponent({
props: {
name: {
type: String as unknown as PropType<K extends string ? K : never>
}
}
});
return cmp;
};
Based on the example provided composableA
expects the argument to be a string
to work as expected, but the type passed to composableB
is an object, which is passed directly to composableA
, not sure exactly what is the intention.
Please remember typed only props do not work in vue at runtime! Vue requires actual props to be passed to the component, for validation, otherwise there's no difference between props or attributes.
Based on that there's a workaround, I personally think passing props as argument should only be when using compiler helper defineProps
, other usages must be done with care.