From efe638178dd3dbbdd242f249d46226753ad046c7 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Tue, 7 Apr 2026 23:37:56 +0800 Subject: [PATCH 1/6] feat(types): infer expose type from SetupContext in defineComponent --- .../dts-test/defineComponent.test-d.tsx | 57 +++++++++++++++++++ .../runtime-core/src/apiDefineComponent.ts | 11 ++-- packages/runtime-core/src/component.ts | 5 +- packages/runtime-core/src/componentOptions.ts | 2 +- 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 6188b102b31..3cebc1cf591 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -3,6 +3,7 @@ import { type ComponentOptions, type ComponentPublicInstance, type PropType, + type Ref, type SetupContext, type Slots, type SlotsType, @@ -1175,6 +1176,62 @@ describe('componentOptions setup should be `SetupContext`', () => { ) }) +describe('infer expose from `SetupContext`', () => { + // options + const Foo = defineComponent({ + setup( + _props: { foo: number }, + _ctx: SetupContext }>, + ) {}, + render() { + expectType(this.bar) + }, + }) + const foo = {} as InstanceType + expectType>(false) + expectType(foo.bar) + + const Bar = defineComponent({ + setup( + _props: { foo: 1 }, + _ctx: { + expose: (exposed: { bar: Ref }) => void + }, + ) {}, + render() { + expectType(this.bar) + }, + }) + const bar = {} as InstanceType + expectType>(false) + expectType(bar.bar) + + // functional + const Baz = defineComponent( + ( + _props: { foo: T }, + _ctx: SetupContext }>, + ) => + () => [], + ) + const baz = new Baz({ foo: 1 }) + expectType>(false) + expectType(baz.bar) + + const Qux = defineComponent( + ( + _props: { foo: T }, + _ctx: { + expose: (exposed: { bar: Ref }) => void + }, + ) => + () => [], + ) + const qux = new Qux({ foo: 1 }) + expectType>(false) + expectType(qux.bar) +}) + describe('extract instance type', () => { const Base = defineComponent({ props: { diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 4865c3b4ea4..f8f1410aade 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -116,13 +116,14 @@ export type DefineSetupFnComponent< P extends Record, E extends EmitsOptions = {}, S extends SlotsType = SlotsType, + B extends Record = {}, Props = P & EmitsToProps, PP = PublicProps, > = new ( props: Props & PP, ) => CreateComponentPublicInstanceWithMixins< Props, - {}, + B, {}, {}, {}, @@ -151,22 +152,24 @@ export function defineComponent< E extends EmitsOptions = {}, EE extends string = string, S extends SlotsType = {}, + B extends Record = {}, >( setup: ( props: Props, - ctx: SetupContext, + ctx: SetupContext, ) => RenderFunction | Promise, options?: Pick & { props?: (keyof NoInfer)[] emits?: E | EE[] slots?: S }, -): DefineSetupFnComponent +): DefineSetupFnComponent export function defineComponent< Props extends Record, E extends EmitsOptions = {}, EE extends string = string, S extends SlotsType = {}, + B extends Record = {}, >( setup: ( props: Props, @@ -177,7 +180,7 @@ export function defineComponent< emits?: E | EE[] slots?: S }, -): DefineSetupFnComponent +): DefineSetupFnComponent // overload 2: defineComponent with options object, infer props from options export function defineComponent< diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index c46741bee80..c3a7de8300e 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -288,14 +288,13 @@ export type LifecycleHook = (TFn & SchedulerJob)[] | null export type SetupContext< E = EmitsOptions, S extends SlotsType = {}, + Exposed extends Record = {}, > = E extends any ? { attrs: Attrs slots: UnwrapSlotsType emit: EmitFn - expose: = Record>( - exposed?: Exposed, - ) => void + expose: (exposed?: Exposed) => void } : never diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 66d1050e12a..db3f500d05c 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -141,7 +141,7 @@ export interface ComponentOptionsBase< > > >, - ctx: SetupContext, + ctx: SetupContext, ) => Promise | RawBindings | RenderFunction | void name?: string template?: string | object // can be a direct DOM node From cd1c9c438fdcc6e938cdd72c2dd8cebb42dedb9e Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Tue, 7 Apr 2026 23:41:06 +0800 Subject: [PATCH 2/6] chore: update --- packages-private/dts-test/defineComponent.test-d.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 3cebc1cf591..9dfd34a2fc3 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -1193,7 +1193,7 @@ describe('infer expose from `SetupContext`', () => { const Bar = defineComponent({ setup( - _props: { foo: 1 }, + _props: { foo: number }, _ctx: { expose: (exposed: { bar: Ref }) => void }, @@ -1210,7 +1210,7 @@ describe('infer expose from `SetupContext`', () => { const Baz = defineComponent( ( _props: { foo: T }, - _ctx: SetupContext }>, + _ctx: SetupContext }>, ) => () => [], ) @@ -1222,7 +1222,7 @@ describe('infer expose from `SetupContext`', () => { ( _props: { foo: T }, _ctx: { - expose: (exposed: { bar: Ref }) => void + expose: (exposed: { bar: Ref }) => void }, ) => () => [], From 297c44e4c52b77c464fc9c725b664ef0267d7675 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Wed, 8 Apr 2026 01:01:36 +0800 Subject: [PATCH 3/6] chore: update --- packages-private/dts-test/defineComponent.test-d.tsx | 12 ++++++++---- packages/runtime-core/src/component.ts | 2 +- packages/runtime-core/src/componentOptions.ts | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 9dfd34a2fc3..130e1b7eb57 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -1182,7 +1182,9 @@ describe('infer expose from `SetupContext`', () => { setup( _props: { foo: number }, _ctx: SetupContext }>, - ) {}, + ) { + return () => <> + }, render() { expectType(this.bar) }, @@ -1197,7 +1199,9 @@ describe('infer expose from `SetupContext`', () => { _ctx: { expose: (exposed: { bar: Ref }) => void }, - ) {}, + ) { + return () => <> + }, render() { expectType(this.bar) }, @@ -1212,7 +1216,7 @@ describe('infer expose from `SetupContext`', () => { _props: { foo: T }, _ctx: SetupContext }>, ) => - () => [], + () => <>, ) const baz = new Baz({ foo: 1 }) expectType>(false) @@ -1225,7 +1229,7 @@ describe('infer expose from `SetupContext`', () => { expose: (exposed: { bar: Ref }) => void }, ) => - () => [], + () => <>, ) const qux = new Qux({ foo: 1 }) expectType>(false) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index c3a7de8300e..300b370b61a 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -288,7 +288,7 @@ export type LifecycleHook = (TFn & SchedulerJob)[] | null export type SetupContext< E = EmitsOptions, S extends SlotsType = {}, - Exposed extends Record = {}, + Exposed = {}, > = E extends any ? { attrs: Attrs diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index db3f500d05c..c795699684c 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -141,7 +141,7 @@ export interface ComponentOptionsBase< > > >, - ctx: SetupContext, + ctx: SetupContext, ) => Promise | RawBindings | RenderFunction | void name?: string template?: string | object // can be a direct DOM node From e460ba3098f433c85a043f5063e791946dd66af2 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 10 Apr 2026 07:01:24 +0800 Subject: [PATCH 4/6] chore: update test --- .../dts-test/defineComponent.test-d.tsx | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 130e1b7eb57..10d60c26469 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -13,6 +13,7 @@ import { h, reactive, ref, + shallowRef, withKeys, withModifiers, } from 'vue' @@ -1180,9 +1181,10 @@ describe('infer expose from `SetupContext`', () => { // options const Foo = defineComponent({ setup( - _props: { foo: number }, - _ctx: SetupContext }>, + props: { foo: number }, + ctx: SetupContext }>, ) { + ctx.expose({ bar: ref(props.foo) }) return () => <> }, render() { @@ -1195,11 +1197,12 @@ describe('infer expose from `SetupContext`', () => { const Bar = defineComponent({ setup( - _props: { foo: number }, - _ctx: { + props: { foo: number }, + ctx: { expose: (exposed: { bar: Ref }) => void }, ) { + ctx.expose({ bar: ref(props.foo) }) return () => <> }, render() { @@ -1213,10 +1216,12 @@ describe('infer expose from `SetupContext`', () => { // functional const Baz = defineComponent( ( - _props: { foo: T }, - _ctx: SetupContext }>, - ) => - () => <>, + props: { foo: T }, + ctx: SetupContext }>, + ) => { + ctx.expose({ bar: shallowRef(props.foo) }) + return () => <> + }, ) const baz = new Baz({ foo: 1 }) expectType>(false) @@ -1224,12 +1229,14 @@ describe('infer expose from `SetupContext`', () => { const Qux = defineComponent( ( - _props: { foo: T }, - _ctx: { + props: { foo: T }, + ctx: { expose: (exposed: { bar: Ref }) => void }, - ) => - () => <>, + ) => { + ctx.expose({ bar: shallowRef(props.foo) }) + return () => <> + }, ) const qux = new Qux({ foo: 1 }) expectType>(false) From 4d0fb2cec6a782290ce1b19710f900c1484389a2 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Sun, 12 Apr 2026 12:30:10 +0800 Subject: [PATCH 5/6] chore: update --- packages-private/dts-test/defineComponent.test-d.tsx | 12 ++++++++++++ packages/runtime-core/src/apiDefineComponent.ts | 2 +- packages/runtime-core/src/component.ts | 2 +- packages/runtime-core/src/componentOptions.ts | 4 +++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 10d60c26469..296b1947442 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -1195,6 +1195,18 @@ describe('infer expose from `SetupContext`', () => { expectType>(false) expectType(foo.bar) + const Foo1 = defineComponent({ + setup(props: { foo: number }, _ctx: SetupContext) { + return { bar: ref(props.foo) } + }, + render() { + expectType(this.bar) + }, + }) + const foo1 = {} as InstanceType + expectType>(false) + expectType(foo1.bar) + const Bar = defineComponent({ setup( props: { foo: number }, diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index f8f1410aade..c36ea106a5e 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -173,7 +173,7 @@ export function defineComponent< >( setup: ( props: Props, - ctx: SetupContext, + ctx: SetupContext, ) => RenderFunction | Promise, options?: Pick & { props?: ComponentObjectPropsOptions diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 300b370b61a..ae989102f60 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -288,7 +288,7 @@ export type LifecycleHook = (TFn & SchedulerJob)[] | null export type SetupContext< E = EmitsOptions, S extends SlotsType = {}, - Exposed = {}, + Exposed extends Record = Record, > = E extends any ? { attrs: Attrs diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index c795699684c..c39c40974e2 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -141,7 +141,9 @@ export interface ComponentOptionsBase< > > >, - ctx: SetupContext, + ctx: SetupContext & { + expose: (exposed?: Record | RawBindings) => void + }, ) => Promise | RawBindings | RenderFunction | void name?: string template?: string | object // can be a direct DOM node From 82407a40d14a113f258a1d3e2dfce27d8919be2d Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Sun, 12 Apr 2026 15:07:47 +0800 Subject: [PATCH 6/6] chore: update --- .../dts-test/defineComponent.test-d.tsx | 47 ------------------- packages/runtime-core/src/componentOptions.ts | 4 +- 2 files changed, 1 insertion(+), 50 deletions(-) diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 296b1947442..ff62aabb072 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -1178,53 +1178,6 @@ describe('componentOptions setup should be `SetupContext`', () => { }) describe('infer expose from `SetupContext`', () => { - // options - const Foo = defineComponent({ - setup( - props: { foo: number }, - ctx: SetupContext }>, - ) { - ctx.expose({ bar: ref(props.foo) }) - return () => <> - }, - render() { - expectType(this.bar) - }, - }) - const foo = {} as InstanceType - expectType>(false) - expectType(foo.bar) - - const Foo1 = defineComponent({ - setup(props: { foo: number }, _ctx: SetupContext) { - return { bar: ref(props.foo) } - }, - render() { - expectType(this.bar) - }, - }) - const foo1 = {} as InstanceType - expectType>(false) - expectType(foo1.bar) - - const Bar = defineComponent({ - setup( - props: { foo: number }, - ctx: { - expose: (exposed: { bar: Ref }) => void - }, - ) { - ctx.expose({ bar: ref(props.foo) }) - return () => <> - }, - render() { - expectType(this.bar) - }, - }) - const bar = {} as InstanceType - expectType>(false) - expectType(bar.bar) - // functional const Baz = defineComponent( ( diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index c39c40974e2..66d1050e12a 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -141,9 +141,7 @@ export interface ComponentOptionsBase< > > >, - ctx: SetupContext & { - expose: (exposed?: Record | RawBindings) => void - }, + ctx: SetupContext, ) => Promise | RawBindings | RenderFunction | void name?: string template?: string | object // can be a direct DOM node