Skip to content

fix(types): keep optional props when a runtime prop uses a generic PropType - #15523

Merged
edison1105 merged 1 commit into
vuejs:mainfrom
ondrhn:fix/generic-proptype-keys
Sep 17, 2026
Merged

edison1105 merged 1 commit into
vuejs:mainfrom
ondrhn:fix/generic-proptype-keys

Conversation

@ondrhn

@ondrhn ondrhn commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

close #9546

Inside <script setup lang="ts" generic="T">, a runtime props object that uses Object as PropType<T> lost every prop that was not required: true (even unrelated ones like qux: String), and the ones that survived never resolved to T.

Cause. PropMethod<T> is a conditional type on T. When T is a type parameter that conditional is deferred, which makes the whole PropType<T> union a generic type. RequiredKeys / OptionalKeys then defer for the entire options object, so ExtractPropTypes yields a generic mapped type and property access fails for everything on the optional side. InferPropType's [T] extends [Prop<infer V, infer D>] was deferred for the same reason.

Fix.

  • Make PropMethod a plain object type. It only exists so Function as PropType<() => void> type-checks; the conditional never changed what a cast accepts, since (): T still has to be comparable with the constructor's call signature.
  • In InferPropType, fall back to V when no default type was inferred (unknown extends D). For concrete types the result is unchanged; for a type parameter it keeps the deferred type's constraint at V, so props.bar is assignable to T.

The added test is the case @pikax asked for in #9652, plus a bare Object as PropType<T> prop, an unrelated String prop, and passing props where { bar: T } is expected (#9277). It fails on main with 5 errors and passes with this change; the rest of dts-test and pnpm check are unaffected.

Summary by CodeRabbit

  • Bug Fixes

    • Improved TypeScript inference for component props declared with generic PropType values.
    • Optional, required, defaulted, and untyped props now receive more accurate inferred types, including appropriate null and undefined handling.
    • Generic prop definitions remain correctly assignable to expected prop structures.
  • Tests

    • Added coverage validating inferred prop names and types across common runtime declaration patterns.

…opType

`PropMethod<T>` was a conditional type on `T`, so `PropType<T>` became a
deferred generic type inside `<script setup generic="T">`. That deferred
`RequiredKeys` / `OptionalKeys` for the whole options object and dropped
every non-required prop (even plain `String` ones) from the inferred
props. The prop value type also never resolved to `T`.

Make `PropMethod` unconditional and fall back to `V` when no default type
is inferred, so keys resolve eagerly and the value stays assignable to `T`.

close vuejs#9546
@coderabbitai

coderabbitai Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: cc2101aa-cf17-48f7-827b-084e5b6212f1

📥 Commits

Reviewing files that changed from the base of the PR and between 5409708 and 7e3b098.

📒 Files selected for processing (2)
  • packages-private/dts-test/setupHelpers.test-d.ts
  • packages/runtime-core/src/componentProps.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Generic runtime prop declarations now retain deferred generic types during prop extraction. The change updates constructor and default inference types and adds type tests for optional, required, defaulted, and built-in props.

Changes

Generic prop inference

Layer / File(s) Summary
Update generic prop inference
packages/runtime-core/src/componentProps.ts
PropMethod no longer uses a conditional branch that resolves generic types to never. Default inference now preserves the value type when the default type is unknown.
Validate extracted prop types
packages-private/dts-test/setupHelpers.test-d.ts
Type tests verify inferred keys, value types, optionality, defaults, and assignability for generic runtime prop declarations.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: edison1105

Merge Risk: ⚪ Minimal · up to 7e3b0

The generic runtime prop inference fix includes coverage for required, optional, defaulted, and built-in props, with no actionable merge risk identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preserving optional props when runtime declarations use a generic PropType.
Linked Issues check ✅ Passed Issue #9546 requires runtime defineProps declarations with generic PropType<T> to preserve all props and infer their generic types. componentProps.ts makes PropMethod<T> unconditional, which p…
Out of Scope Changes check ✅ Passed The pull request changes only the runtime prop type inference declarations and adds focused defineProps type tests for issue #9546. The changes support the linked issue objectives and do not show un…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@pkg-pr-new

pkg-pr-new Bot commented Sep 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@15523
npm i https://pkg.pr.new/@vue/compiler-core@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@15523
npm i https://pkg.pr.new/@vue/compiler-dom@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@15523
npm i https://pkg.pr.new/@vue/compiler-sfc@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@15523
npm i https://pkg.pr.new/@vue/compiler-ssr@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@15523
npm i https://pkg.pr.new/@vue/reactivity@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@15523
npm i https://pkg.pr.new/@vue/runtime-core@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@15523
npm i https://pkg.pr.new/@vue/runtime-dom@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@15523
npm i https://pkg.pr.new/@vue/server-renderer@15523
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@15523
npm i https://pkg.pr.new/@vue/shared@15523
yarn add https://pkg.pr.new/@vue/[email protected]

vue

pnpm add https://pkg.pr.new/vue@15523
npm i https://pkg.pr.new/vue@15523
yarn add https://pkg.pr.new/[email protected]

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@15523
npm i https://pkg.pr.new/@vue/compat@15523
yarn add https://pkg.pr.new/@vue/[email protected]

commit: 7e3b098

@github-actions

Copy link
Copy Markdown

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 109 kB 41.1 kB 36.8 kB
vue.global.prod.js 168 kB 61.3 kB 54.4 kB

Usages

Name Size Gzip Brotli
createApp (CAPI only) 50.3 kB 19.4 kB 17.8 kB
createApp 58.4 kB 22.5 kB 20.6 kB
createSSRApp 63.2 kB 24.5 kB 22.3 kB
defineCustomElement 64.7 kB 24.4 kB 22.3 kB
overall 72.9 kB 27.8 kB 25.3 kB

@edison1105

Copy link
Copy Markdown
Member

/ecosystem-ci run

@edison1105 edison1105 added the ready to merge The PR is ready to be merged. label Sep 15, 2026
@vue-bot

vue-bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

📝 Ran ecosystem CI: Open

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

@edison1105
edison1105 merged commit 243aabc into vuejs:main Sep 17, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready to merge The PR is ready to be merged. scope: types

Projects

None yet

Development

Successfully merging this pull request may close these issues.

defineProps options object loses properties when using generics

3 participants