Skip to content

feat(types): infer expose type from SetupContext in defineComponent#14693

Open
zhiyuanzmj wants to merge 6 commits intovuejs:mainfrom
zhiyuanzmj:setup-context-expose
Open

feat(types): infer expose type from SetupContext in defineComponent#14693
zhiyuanzmj wants to merge 6 commits intovuejs:mainfrom
zhiyuanzmj:setup-context-expose

Conversation

@zhiyuanzmj
Copy link
Copy Markdown
Member

@zhiyuanzmj zhiyuanzmj commented Apr 7, 2026

const Qux = defineComponent(
  <T,>(
    props: { foo: T },
    ctx: {
      expose: (exposed: { bar: Ref<T> }) => void
    },
  ) => () => [],
)
const qux = new Qux({ foo: 1 })
expectType<number>(qux.bar)

Summary by CodeRabbit

  • Tests

    • Added TypeScript type inference tests for exposed component properties with SetupContext
  • Improvements

    • Enhanced TypeScript type inference for component expose functionality in defineComponent
    • Improved type safety and IDE autocomplete support for exposed component properties

Copilot AI review requested due to automatic review settings April 7, 2026 15:39
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 15644ae5-ebb7-44ba-9b04-99087e2544dc

📥 Commits

Reviewing files that changed from the base of the PR and between 4d0fb2c and 82407a4.

📒 Files selected for processing (1)
  • packages-private/dts-test/defineComponent.test-d.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages-private/dts-test/defineComponent.test-d.tsx

📝 Walkthrough

Walkthrough

The changes enhance type inference for exposed properties in Vue components by introducing a new generic parameter to the SetupContext to track explicitly exposed types, then threading this parameter through the DefineSetupFnComponent and defineComponent APIs for accurate public instance typing.

Changes

Cohort / File(s) Summary
SetupContext and DefineComponent Type Updates
packages/runtime-core/src/component.ts, packages/runtime-core/src/apiDefineComponent.ts
Added Exposed generic parameter to SetupContext (defaulting to Record<string, any>), changing the expose property from a per-call generic function to a non-generic function typed with the Exposed parameter. Updated DefineSetupFnComponent and defineComponent setup function overloads to accept and propagate the new B (bindings) generic parameter through the type chain.
Expose Type Inference Tests
packages-private/dts-test/defineComponent.test-d.tsx
Added comprehensive TypeScript type tests validating that expose correctly infers exposed property types in both typed SetupContext and manually-shaped context scenarios, confirming that exposed properties are inferred as specific types rather than any.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested reviewers

  • jh-leong
  • johnsoncodehk
  • KazariEX

Poem

🐰 With generic parameters we now thread,
Exposed types flow where expose is spread,
No more mysteries wrapped in any,
SetupContext knows what you're revealing—
Type-safety brings such a wholesome feeling! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(types): infer expose type from SetupContext in defineComponent' accurately captures the main change: enabling TypeScript type inference for exposed properties from SetupContext in defineComponent.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 7, 2026

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@14693
npm i https://pkg.pr.new/@vue/compiler-core@14693
yarn add https://pkg.pr.new/@vue/compiler-core@14693.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@14693
npm i https://pkg.pr.new/@vue/compiler-dom@14693
yarn add https://pkg.pr.new/@vue/compiler-dom@14693.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@14693
npm i https://pkg.pr.new/@vue/compiler-sfc@14693
yarn add https://pkg.pr.new/@vue/compiler-sfc@14693.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@14693
npm i https://pkg.pr.new/@vue/compiler-ssr@14693
yarn add https://pkg.pr.new/@vue/compiler-ssr@14693.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@14693
npm i https://pkg.pr.new/@vue/reactivity@14693
yarn add https://pkg.pr.new/@vue/reactivity@14693.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@14693
npm i https://pkg.pr.new/@vue/runtime-core@14693
yarn add https://pkg.pr.new/@vue/runtime-core@14693.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@14693
npm i https://pkg.pr.new/@vue/runtime-dom@14693
yarn add https://pkg.pr.new/@vue/runtime-dom@14693.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@14693
npm i https://pkg.pr.new/@vue/server-renderer@14693
yarn add https://pkg.pr.new/@vue/server-renderer@14693.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@14693
npm i https://pkg.pr.new/@vue/shared@14693
yarn add https://pkg.pr.new/@vue/shared@14693.tgz

vue

pnpm add https://pkg.pr.new/vue@14693
npm i https://pkg.pr.new/vue@14693
yarn add https://pkg.pr.new/vue@14693.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@14693
npm i https://pkg.pr.new/@vue/compat@14693
yarn add https://pkg.pr.new/@vue/compat@14693.tgz

commit: 82407a4

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 105 kB 39.9 kB 35.8 kB
vue.global.prod.js 164 kB 59.9 kB 53.2 kB

Usages

Name Size Gzip Brotli
createApp (CAPI only) 48.3 kB 18.8 kB 17.2 kB
createApp 56.5 kB 21.8 kB 20 kB
createSSRApp 60.7 kB 23.6 kB 21.5 kB
defineCustomElement 62.6 kB 23.8 kB 21.7 kB
overall 71 kB 27.2 kB 24.7 kB

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Vue’s public TypeScript typings so that the type of values passed to ctx.expose() can be carried through SetupContext and (intendedly) inferred into the component instance type produced by defineComponent.

Changes:

  • Extend SetupContext with a new generic parameter representing the exposed object type, and simplify the expose function signature to use it.
  • Thread a new generic (B) through defineComponent’s setup-function overloads to propagate the exposed/bindings type into the returned instance type.
  • Add DTS tests asserting that exposed properties can be inferred from SetupContext (and from a minimal { expose(...) } context shape).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
packages/runtime-core/src/componentOptions.ts Changes the setup context type to pass a third SetupContext generic derived from RawBindings.
packages/runtime-core/src/component.ts Adds Exposed generic to SetupContext and updates expose typing to use it.
packages/runtime-core/src/apiDefineComponent.ts Adds B generic to DefineSetupFnComponent and defineComponent overloads to propagate exposed/bindings typing.
packages-private/dts-test/defineComponent.test-d.tsx Adds DTS tests for inferring exposed properties from SetupContext.
Comments suppressed due to low confidence (1)

packages/runtime-core/src/apiDefineComponent.ts:177

  • The second defineComponent overload returns DefineSetupFnComponent<Props, E, S, B> but its setup signature still types ctx as SetupContext<E, S> (missing the B argument). This prevents inferring (or even specifying) the exposed/bindings type B when using runtime props options (the 2nd argument) and can make SetupContext<..., ..., B> annotations fail to match this overload. Update this overload to use ctx: SetupContext<E, S, B> to keep the generics consistent and allow the new inference behavior in all overloads.
  Props extends Record<string, any>,
  E extends EmitsOptions = {},
  EE extends string = string,
  S extends SlotsType = {},
  B extends Record<string, any> = {},
>(
  setup: (
    props: Props,
    ctx: SetupContext<E, S>,
  ) => RenderFunction | Promise<RenderFunction>,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/runtime-core/src/componentOptions.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/runtime-core/src/apiDefineComponent.ts (1)

167-183: ⚠️ Potential issue | 🟡 Minor

Inconsistent SetupContext usage between overloads.

The second overload (lines 167-183) declares the B generic parameter but uses SetupContext<E, S> on line 176 instead of SetupContext<E, S, B>. This means exposed types won't be inferred when using object-style props options.

Compare with the first overload (lines 150-166) which correctly uses SetupContext<E, S, B> on line 159.

🔧 Proposed fix
 export function defineComponent<
   Props extends Record<string, any>,
   E extends EmitsOptions = {},
   EE extends string = string,
   S extends SlotsType = {},
   B extends Record<string, any> = {},
 >(
   setup: (
     props: Props,
-    ctx: SetupContext<E, S>,
+    ctx: SetupContext<E, S, B>,
   ) => RenderFunction | Promise<RenderFunction>,
   options?: Pick<ComponentOptions, 'name' | 'inheritAttrs'> & {
     props?: ComponentObjectPropsOptions<Props>
     emits?: E | EE[]
     slots?: S
   },
 ): DefineSetupFnComponent<Props, E, S, B>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/runtime-core/src/apiDefineComponent.ts` around lines 167 - 183, The
second overload of defineComponent incorrectly uses SetupContext<E, S> while it
declares the B generic, so exposed types won't be inferred for object-style
props; update the setup parameter's context type in that overload from
SetupContext<E, S> to SetupContext<E, S, B> (keeping the rest of the
signature—props?: ComponentObjectPropsOptions<Props>, emits?: E | EE[], slots?:
S—and return type DefineSetupFnComponent<Props, E, S, B>—so the B generic is
actually propagated to the SetupContext).
🧹 Nitpick comments (1)
packages/runtime-core/src/componentOptions.ts (1)

144-144: Clarify the relationship between RawBindings and exposed types.

The third generic parameter to SetupContext is set to RawBindings & {}. Based on the test cases, the type inference flows from an explicitly typed ctx parameter annotation back to the component instance. This works because TypeScript infers RawBindings from the ctx parameter's type when the setup function is explicitly typed.

However, this creates a semantic coupling where RawBindings (traditionally the setup return type) also becomes the exposed type. Consider documenting this relationship or adding a comment explaining why RawBindings is used here rather than a dedicated Exposed parameter.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/runtime-core/src/componentOptions.ts` at line 144, The current use
of RawBindings in the SetupContext third generic (ctx: SetupContext<E, S,
RawBindings & {}>) ties the exposed type to the setup return type and may
confuse readers; add a concise comment above this signature in
componentOptions.ts explaining that RawBindings is intentionally reused here so
that TypeScript can infer the exposed type from an explicitly typed ctx
parameter (i.e., why we pass RawBindings & {} instead of a separate Exposed
generic), and note any implications for type inference and semantic coupling
between setup return types and exposed instance types (referencing SetupContext
and RawBindings in the comment).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/runtime-core/src/apiDefineComponent.ts`:
- Around line 167-183: The second overload of defineComponent incorrectly uses
SetupContext<E, S> while it declares the B generic, so exposed types won't be
inferred for object-style props; update the setup parameter's context type in
that overload from SetupContext<E, S> to SetupContext<E, S, B> (keeping the rest
of the signature—props?: ComponentObjectPropsOptions<Props>, emits?: E | EE[],
slots?: S—and return type DefineSetupFnComponent<Props, E, S, B>—so the B
generic is actually propagated to the SetupContext).

---

Nitpick comments:
In `@packages/runtime-core/src/componentOptions.ts`:
- Line 144: The current use of RawBindings in the SetupContext third generic
(ctx: SetupContext<E, S, RawBindings & {}>) ties the exposed type to the setup
return type and may confuse readers; add a concise comment above this signature
in componentOptions.ts explaining that RawBindings is intentionally reused here
so that TypeScript can infer the exposed type from an explicitly typed ctx
parameter (i.e., why we pass RawBindings & {} instead of a separate Exposed
generic), and note any implications for type inference and semantic coupling
between setup return types and exposed instance types (referencing SetupContext
and RawBindings in the comment).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d7383bd-daca-4911-85b0-d5f06262a92d

📥 Commits

Reviewing files that changed from the base of the PR and between 9a2eb53 and cd1c9c4.

📒 Files selected for processing (4)
  • packages-private/dts-test/defineComponent.test-d.tsx
  • packages/runtime-core/src/apiDefineComponent.ts
  • packages/runtime-core/src/component.ts
  • packages/runtime-core/src/componentOptions.ts

@vuejs vuejs deleted a comment from zhiyuanzmj Apr 12, 2026
@zhiyuanzmj
Copy link
Copy Markdown
Member Author

/ecosystem-ci run

@vuejs vuejs deleted a comment from zhiyuanzmj Apr 12, 2026
@vue-bot
Copy link
Copy Markdown
Contributor

vue-bot commented Apr 12, 2026

📝 Ran ecosystem CI: Open

suite result latest scheduled
radix-vue success success
nuxt success success
language-tools failure failure
primevue success success
pinia success success
vue-i18n success success
vuetify success success
vitepress success success
vue-simple-compiler success success
vueuse success success
vant success success
test-utils success success
vite-plugin-vue success success
vue-macros success success
quasar success success
router success success

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants