From 7f374a3f5ad69d5471548b4d2c76b01d691d0178 Mon Sep 17 00:00:00 2001 From: Danil Nemov Date: Sun, 30 Mar 2025 22:14:41 +0300 Subject: [PATCH 1/2] fix(types): make generics with runtime props in defineComponent work --- .../dts-test/defineComponent.test-d.tsx | 99 ++++++++++++++++--- .../runtime-core/src/apiDefineComponent.ts | 2 +- 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 1967668dceb..620fd19152f 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -1402,7 +1402,7 @@ describe('function syntax w/ emits', () => { describe('function syntax w/ runtime props', () => { // with runtime props, the runtime props must match // manual type declaration - defineComponent( + const Comp1 = defineComponent( (_props: { msg: string }) => { return () => {} }, @@ -1411,7 +1411,34 @@ describe('function syntax w/ runtime props', () => { }, ) + // @ts-expect-error bar isn't specified in props definition defineComponent( + (_props: { msg: string }) => { + return () => {} + }, + { + props: ['msg', 'bar'], + }, + ) + + defineComponent( + (_props: { msg: string; bar: string }) => { + return () => {} + }, + { + props: ['msg'], + }, + ) + + expectType() + // @ts-expect-error msg type is incorrect + expectType() + // @ts-expect-error msg is missing + expectType() + // @ts-expect-error bar doesn't exist + expectType() + + const Comp2 = defineComponent( (_props: { msg: T }) => { return () => {} }, @@ -1420,7 +1447,36 @@ describe('function syntax w/ runtime props', () => { }, ) + // @ts-expect-error bar isn't specified in props definition defineComponent( + (_props: { msg: T }) => { + return () => {} + }, + { + props: ['msg', 'bar'], + }, + ) + + defineComponent( + (_props: { msg: T; bar: T }) => { + return () => {} + }, + { + props: ['msg'], + }, + ) + + expectType() + expectType( msg="1" />) + // @ts-expect-error msg type is incorrect + expectType() + // @ts-expect-error msg is missing + expectType() + // @ts-expect-error bar doesn't exist + expectType() + + // Note: generics aren't supported with object runtime props + const Comp3 = defineComponent( (_props: { msg: T }) => { return () => {} }, @@ -1431,37 +1487,58 @@ describe('function syntax w/ runtime props', () => { }, ) - // @ts-expect-error string prop names don't match defineComponent( - (_props: { msg: string }) => { + // @ts-expect-error bar isn't specified in props definition + (_props: { msg: T }) => { return () => {} }, { - props: ['bar'], + props: { + bar: String, + }, }, ) defineComponent( - (_props: { msg: string }) => { + // @ts-expect-error generics aren't supported with object runtime props + (_props: { msg: T; bar: T }) => { return () => {} }, { props: { - // @ts-expect-error prop type mismatch - msg: Number, + msg: String, }, }, ) - // @ts-expect-error prop keys don't match + expectType() + // @ts-expect-error generics aren't supported with object runtime props + expectType( msg="1" />) + // @ts-expect-error msg type is incorrect + expectType() + // @ts-expect-error msg is missing + expectType() + // @ts-expect-error bar doesn't exist + expectType() + + // @ts-expect-error string prop names don't match defineComponent( - (_props: { msg: string }, ctx) => { + (_props: { msg: string }) => { + return () => {} + }, + { + props: ['bar'], + }, + ) + + defineComponent( + (_props: { msg: string }) => { return () => {} }, { props: { - msg: String, - bar: String, + // @ts-expect-error prop type mismatch + msg: Number, }, }, ) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 2ce870f0141..255d383ff47 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -157,7 +157,7 @@ export function defineComponent< ctx: SetupContext, ) => RenderFunction | Promise, options?: Pick & { - props?: (keyof Props)[] + props?: (keyof NoInfer)[] emits?: E | EE[] slots?: S }, From b8cc94b6eccb85982ac1857c338908ff168e0488 Mon Sep 17 00:00:00 2001 From: "danil.nemov" Date: Wed, 11 Jun 2025 20:27:07 +0300 Subject: [PATCH 2/2] fix: make inferring from runtime props possible in case props types aren't specified --- .../dts-test/defineComponent.test-d.tsx | 99 +++++++++++-------- .../runtime-core/src/apiDefineComponent.ts | 16 +++ 2 files changed, 74 insertions(+), 41 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 620fd19152f..f8a9f921859 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -2,6 +2,7 @@ import { type Component, type ComponentOptions, type ComponentPublicInstance, + type DefineSetupFnComponent, type PropType, type SetupContext, type Slots, @@ -1411,6 +1412,14 @@ describe('function syntax w/ runtime props', () => { }, ) + expectType() + // @ts-expect-error msg type is incorrect + expectType() + // @ts-expect-error msg is missing + expectType() + // @ts-expect-error bar doesn't exist + expectType() + // @ts-expect-error bar isn't specified in props definition defineComponent( (_props: { msg: string }) => { @@ -1430,13 +1439,15 @@ describe('function syntax w/ runtime props', () => { }, ) - expectType() - // @ts-expect-error msg type is incorrect - expectType() - // @ts-expect-error msg is missing - expectType() - // @ts-expect-error bar doesn't exist - expectType() + // @ts-expect-error string prop names don't match + defineComponent( + (_props: { msg: string }) => { + return () => {} + }, + { + props: ['bar'], + }, + ) const Comp2 = defineComponent( (_props: { msg: T }) => { @@ -1447,8 +1458,16 @@ describe('function syntax w/ runtime props', () => { }, ) - // @ts-expect-error bar isn't specified in props definition - defineComponent( + expectType() + expectType( msg="1" />) + // @ts-expect-error msg type is incorrect + expectType() + // @ts-expect-error msg is missing + expectType() + // @ts-expect-error bar doesn't exist + expectType() + + const Comp3 = defineComponent( (_props: { msg: T }) => { return () => {} }, @@ -1457,6 +1476,12 @@ describe('function syntax w/ runtime props', () => { }, ) + // This is not the preferred behavior because it's better to see a typescript error, + // but this is a compromise to resolve a relatively worse problem - + // not inferring props types from runtime props when the types are not explicitly set. + // See #13119#discussion_r2137831991 + expectType>(Comp3) + defineComponent( (_props: { msg: T; bar: T }) => { return () => {} @@ -1466,17 +1491,9 @@ describe('function syntax w/ runtime props', () => { }, ) - expectType() - expectType( msg="1" />) - // @ts-expect-error msg type is incorrect - expectType() - // @ts-expect-error msg is missing - expectType() - // @ts-expect-error bar doesn't exist - expectType() - // Note: generics aren't supported with object runtime props - const Comp3 = defineComponent( + // so the props will infer the runtime props' types + const Comp4 = defineComponent( (_props: { msg: T }) => { return () => {} }, @@ -1487,6 +1504,17 @@ describe('function syntax w/ runtime props', () => { }, ) + expectType>(Comp4) + expectType() + // @ts-expect-error generics aren't supported with object runtime props + expectType( msg="1" />) + // @ts-expect-error msg type is incorrect + expectType() + // @ts-expect-error msg is missing + expectType() + // @ts-expect-error bar doesn't exist + expectType() + defineComponent( // @ts-expect-error bar isn't specified in props definition (_props: { msg: T }) => { @@ -1500,46 +1528,35 @@ describe('function syntax w/ runtime props', () => { ) defineComponent( - // @ts-expect-error generics aren't supported with object runtime props - (_props: { msg: T; bar: T }) => { + (_props: { msg: string }) => { return () => {} }, { props: { - msg: String, + // @ts-expect-error prop type mismatch + msg: Number, }, }, ) - expectType() - // @ts-expect-error generics aren't supported with object runtime props - expectType( msg="1" />) - // @ts-expect-error msg type is incorrect - expectType() - // @ts-expect-error msg is missing - expectType() - // @ts-expect-error bar doesn't exist - expectType() - - // @ts-expect-error string prop names don't match - defineComponent( - (_props: { msg: string }) => { + const Comp5 = defineComponent( + _props => { return () => {} }, { - props: ['bar'], + props: ['foo'], }, ) + expectType>(Comp5) + defineComponent( - (_props: { msg: string }) => { + // @ts-expect-error the props type is required when a generic type is present + (_props) => { return () => {} }, { - props: { - // @ts-expect-error prop type mismatch - msg: Number, - }, + props: [], }, ) }) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 255d383ff47..3d9e89a533f 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -146,6 +146,22 @@ type ToResolvedProps = Readonly & // overload 1: direct setup function // (uses user defined props interface) +export function defineComponent< + Props extends Record, + E extends EmitsOptions = {}, + EE extends string = string, + S extends SlotsType = {}, +>( + setup: ( + props: Props, + ctx: SetupContext, + ) => RenderFunction | Promise, + options?: Pick & { + props?: (keyof Props)[] + emits?: E | EE[] + slots?: S + }, +): DefineSetupFnComponent export function defineComponent< Props extends Record, E extends EmitsOptions = {},