What worked for me is this:
import {AllowedComponentProps, Component, VNodeProps} from 'vue';
type ComponentProps<C extends Component> = C extends new (...args: any) => any
? Omit<InstanceType<C>['$props'], keyof VNodeProps | keyof AllowedComponentProps>
: never;
Full working example
import {AllowedComponentProps, Component, PropType, VNodeProps} from 'vue';
type ComponentProps<C extends Component> = C extends new (...args: any) => any
? Omit<InstanceType<C>['$props'], keyof VNodeProps | keyof AllowedComponentProps>
: never;
type Config<C extends Component> = {
component: C;
props: ComponentProps<C>;
};
declare function defineConfig<C extends Component>(config: Config<C>): void;
const TestComponent = defineComponent({
name: 'TestComponent',
props: {
disabled: {
type: Boolean,
},
options: {
type: Array as PropType<{key: string; value: string}[]>,
required: true,
},
},
});
defineConfig({
component: TestComponent,
props: { // TS will throw an error because options is required but missing
// IDE will suggest:
// disabled: boolean | undefined
// options: {key: string; value: string}[]
},
});
Why does it need Omit?
Just InstanceType<C>['$props'] includes properties of VNodeProps and AllowedComponentProps. So, omitting their properties leaves only the actual props of the component.
Why does it need extends?
Without checking if C extends new (...args: any) => any, InstanceType will trigger TS error TS2344:
TS2344: Type 'C' does not satisfy the constraint 'abstract new (...args: any) => any'.