diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index f6793da5f30..29fee07b4c1 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -70,8 +70,9 @@ export type DefineComponent< E, EE, Defaults - > & - PP + > & { + props: PropsOrPropOptions + } & PP // defineComponent is a utility that is primarily used for type inference // when declaring components. Type inference is provided in the component @@ -112,7 +113,7 @@ export function defineComponent< E, EE > -): DefineComponent +): DefineComponent // overload 3: object format with array props declaration // props inferred as { [key in PropNames]?: any } diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 7218d22288b..70cc20b8bcd 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -119,6 +119,8 @@ export type ExtractPropTypes = O extends object ? { [K in keyof O]?: unknown } & // This is needed to keep the relation between the option prop and the props, allowing to use ctrl+click to navigate to the prop options. see: #3656 { [K in RequiredKeys]: InferPropType } & { [K in OptionalKeys]?: InferPropType } + : O extends undefined + ? {} : { [K in string]: any } const enum BooleanFlags { diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 4766bddb4b3..c2d1a2c48f8 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -46,93 +46,97 @@ describe('with object props', () => { type GT = string & { __brand: unknown } - const MyComponent = defineComponent({ - props: { - a: Number, - // required should make property non-void - b: { - type: String, - required: true - }, - e: Function, - h: Boolean, - j: Function as PropType string | undefined)>, - // default value should infer type and make it non-void - bb: { - default: 'hello' - }, - bbb: { - // Note: default function value requires arrow syntax + explicit - // annotation - default: (props: any) => (props.bb as string) || 'foo' - }, - bbbb: { - type: String, - default: undefined - }, - bbbbb: { - type: String, - default: () => undefined - }, - // explicit type casting - cc: Array as PropType, - // required + type casting - dd: { - type: Object as PropType<{ n: 1 }>, - required: true - }, - // return type - ee: Function as PropType<() => string>, - // arguments + object return - ff: Function as PropType<(a: number, b: string) => { a: boolean }>, - // explicit type casting with constructor - ccc: Array as () => string[], - // required + contructor type casting - ddd: { - type: Array as () => string[], - required: true - }, - // required + object return - eee: { - type: Function as PropType<() => { a: string }>, - required: true - }, - // required + arguments + object return - fff: { - type: Function as PropType<(a: number, b: string) => { a: boolean }>, - required: true - }, - hhh: { - type: Boolean, - required: true - }, - // default + type casting - ggg: { - type: String as PropType<'foo' | 'bar'>, - default: 'foo' - }, - // default + function - ffff: { - type: Function as PropType<(a: number, b: string) => { a: boolean }>, - default: (a: number, b: string) => ({ a: a > +b }) - }, - // union + function with different return types - iii: Function as PropType<(() => string) | (() => number)>, - // union + function with different args & same return type - jjj: { - type: Function as PropType< - ((arg1: string) => string) | ((arg1: string, arg2: string) => string) - >, - required: true - }, - kkk: null, - validated: { - type: String, - // validator requires explicit annotation - validator: (val: unknown) => val !== '' - }, - date: Date + const props = { + a: Number, + // required should make property non-void + b: { + type: String, + required: true as true + }, + e: Function, + h: Boolean, + j: Function as PropType string | undefined)>, + // default value should infer type and make it non-void + bb: { + default: 'hello' + }, + bbb: { + // Note: default function value requires arrow syntax + explicit + // annotation + default: (props: any) => (props.bb as string) || 'foo' + }, + bbbb: { + type: String, + default: undefined + }, + bbbbb: { + type: String, + default: () => undefined + }, + // explicit type casting + cc: Array as PropType, + // required + type casting + dd: { + type: Object as PropType<{ n: 1 }>, + required: true as true + }, + // return type + ee: Function as PropType<() => string>, + // arguments + object return + ff: Function as PropType<(a: number, b: string) => { a: boolean }>, + // explicit type casting with constructor + ccc: Array as () => string[], + // required + contructor type casting + ddd: { + type: Array as () => string[], + required: true as true }, + // required + object return + eee: { + type: Function as PropType<() => { a: string }>, + required: true as true + }, + // required + arguments + object return + fff: { + type: Function as PropType<(a: number, b: string) => { a: boolean }>, + required: true as true + }, + hhh: { + type: Boolean, + required: true as true + }, + // default + type casting + ggg: { + type: String as PropType<'foo' | 'bar'>, + default: 'foo' + }, + // default + function + ffff: { + type: Function as PropType<(a: number, b: string) => { a: boolean }>, + default: (a: number, b: string) => ({ a: a > +b }) + }, + // union + function with different return types + iii: Function as PropType<(() => string) | (() => number)>, + // union + function with different args & same return type + jjj: { + type: Function as PropType< + ((arg1: string) => string) | ((arg1: string, arg2: string) => string) + >, + required: true as true + }, + kkk: null, + validated: { + type: String, + // validator requires explicit annotation + validator: (val: unknown) => val !== '' + }, + date: Date + } + + type ExpectedPropsType = typeof props + + const MyComponent = defineComponent({ + props, setup(props) { // type assertion. See https://github.com/SamVerschueren/tsd expectType(props.a) @@ -249,6 +253,8 @@ describe('with object props', () => { expectType(MyComponent) + expectType(MyComponent.props) + // Test TSX expectType(