diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index f9de525c87d..61f968b7634 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -39,7 +39,14 @@ interface PropOptions { export type PropType = PropConstructor | PropConstructor[] -type PropConstructor = { new (...args: any[]): T & object } | { (): T } +type PropConstructor = + | { new (...args: any[]): T & object } + | { (): T } + | PropMethod + +type PropMethod = T extends (...args: any) => any // if is function with args + ? { new (): T; (): T; readonly proptotype: Function } // Create Function like contructor + : never type RequiredKeys = { [K in keyof T]: T[K] extends diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 33f55695569..6838e6db2fc 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -12,11 +12,16 @@ describe('with object props', () => { interface ExpectedProps { a?: number | undefined b: string + e?: Function bb: string cc?: string[] | undefined dd: string[] + ee?: () => string + ff?: (a: number, b: string) => { a: boolean } ccc?: string[] | undefined ddd: string[] + eee: () => { a: string } + fff: (a: number, b: string) => { a: boolean } } type GT = string & { __brand: unknown } @@ -29,6 +34,7 @@ describe('with object props', () => { type: String, required: true }, + e: Function, // default value should infer type and make it non-void bb: { default: 'hello' @@ -40,23 +46,42 @@ describe('with object props', () => { type: Array as PropType, 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 } }, setup(props) { // type assertion. See https://github.com/SamVerschueren/tsd expectType(props.a) expectType(props.b) + expectType(props.e) expectType(props.bb) expectType(props.cc) expectType(props.dd) + expectType(props.ee) + expectType(props.ff) expectType(props.ccc) expectType(props.ddd) + expectType(props.eee) + expectType(props.fff) // props should be readonly expectError((props.a = 1)) @@ -76,11 +101,16 @@ describe('with object props', () => { const props = this.$props expectType(props.a) expectType(props.b) + expectType(props.e) expectType(props.bb) expectType(props.cc) expectType(props.dd) + expectType(props.ee) + expectType(props.ff) expectType(props.ccc) expectType(props.ddd) + expectType(props.eee) + expectType(props.fff) // props should be readonly expectError((props.a = 1)) @@ -88,11 +118,16 @@ describe('with object props', () => { // should also expose declared props on `this` expectType(this.a) expectType(this.b) + expectType(this.e) expectType(this.bb) expectType(this.cc) expectType(this.dd) + expectType(this.ee) + expectType(this.ff) expectType(this.ccc) expectType(this.ddd) + expectType(this.eee) + expectType(this.fff) // props on `this` should be readonly expectError((this.a = 1)) @@ -115,10 +150,14 @@ describe('with object props', () => { a={1} b="b" bb="bb" + e={() => {}} cc={['cc']} dd={['dd']} + ee={() => 'ee'} ccc={['ccc']} ddd={['ddd']} + eee={() => ({ a: 'eee' })} + fff={(a, b) => ({ a: a > +b })} // should allow extraneous as attrs class="bar" // should allow key