PropsFor inferring from DefineComponent variants
What problem does this feature solve?
Within utilities, it can be useful to be able to type the props of a component. For example, a @vue/test-utils
mount call could look like this...
describe('My Component', () => {
async function render(extraProps?: Partial<PropsFor<typeof MyComponent>>) => {
return mount(MyComponent, {
propsData: {
flavour:"vanilla",
sprinkles:true,
...extraProps,
},
});
};
I successfully established PropsFor
from a reference to MyComponent
(an example of a DefineComponent
which used the Options API) like this...
export type PropsFor<T extends DefineComponent<any, any, any>> =
T extends DefineComponent<infer Def, any, any>
? ExtractPropTypes<Def>
: never;
I feel it's impractical for anyone outside the project to maintain such a definition. Matching one defineComponent
signature seems brittle and perhaps depends closely on the overload sequence of defineComponent. For example as soon as I moved away from Options API and start using setup
to implement MyComponent it seems the above implementation breaks.
Also the Vue project may well need to add further defineComponent
signatures over time and it's infeasible to track these changes.
Ideally there would be a canonical representation of PropsFor
which is maintained within the Vue3 repository, so that you can derive the type of Props given any typed reference to a component.
What does the proposed API look like?
type MyProps = PropsFor<typeof MyComponent>
...where MyComponent is defined like...
export default defineComponent({
name: 'MyComponent',
[...]
})
@pikax Didn't you work on a PR that made component definition types canonical to enable this kind of usage?
@pikax Didn't you work on a PR that made component definition types canonical to enable this kind of usage?
I did, but I think is different than what's being requested here, I worked on passing props definition to the component, but because of the typescript limitation of not supporting partial typing on generics.
I think this issue is valid we need those helpers to be provided, not sure about the name but don't have a strong opinion
Update: We are still battling with being unable to consistently establish the props of components in our test suites. Is there an option I've missed somewhere?
Within a test we need to be able to author functions that take multiple different components.
For example, our components often need to be tested as a descendant of an ancestor that implements injection, and which has render-exception handling.
The wrap in this ancestor
utility function inevitably accepts a component and its props. However, because of the complexity of Vue component typing, we can't even establish the type of their props from a component definition.
@cefn: Not sure, but maybe volars vue-component-meta can help.
Hey, thanks for the suggestion.
Having looked at it I feel Volar's tooling nature (consuming the source code and reasoning over the abstract syntax tree) makes it quite a different category than type inference. It will introduce substantial complexity and unnecessary failure modes and could certainly never be used at runtime (although my own primary use case is testing, it's true).
We have been using our own pure-typing props inference definition for 6 months or so but it's effective only for our own components and when we start to use a new Vue feature (e.g. emits) then it breaks since the shape we assume when inferring has to be different.
It's for this reason that an inference definition that reflects knowledge of the internal typing design of a Vue component is needed, and having it in the core means it would track changes.
Makes sense. I did some more digging and found this in volar:
export type ComponentProps<T> =
${vueCompilerOptions.strictTemplates ? '' : 'Record<string, unknown> &'}
GlobalAttrs &
ExtractProps<T>;
(source)
export type ExtractProps<T> =
T extends FunctionalComponent<infer P> ? P
: T extends new (...args: any) => { $props: infer Props } ? Props
: T; // IntrinsicElement
(source)