Skip to content

fix(reactivity): normalize toRef property keys before dep lookup + improve types#14625

Merged
edison1105 merged 5 commits intovuejs:mainfrom
edison1105:edison/fix/12427
Mar 25, 2026
Merged

fix(reactivity): normalize toRef property keys before dep lookup + improve types#14625
edison1105 merged 5 commits intovuejs:mainfrom
edison1105:edison/fix/12427

Conversation

@edison1105
Copy link
Copy Markdown
Member

@edison1105 edison1105 commented Mar 25, 2026

fix #12427
close #12431

Summary by CodeRabbit

  • New Features

    • toRef now supports numeric and symbol property keys for more flexible reactive references and correct value typing.
  • Tests

    • Added tests ensuring triggering numeric- and symbol-keyed refs causes dependent effects to re-run exactly once and that symbol identity is preserved.
  • Types

    • Added type-check tests validating toRef behavior for arrays and tuples (indexed access yields correct Ref types).

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 25, 2026

📝 Walkthrough

Walkthrough

Widened toRef/toRefs typings and runtime to accept PropertyKey keys (string | number | symbol), normalized internal ref keys (preserving symbols, coercing non-symbols), adjusted array index handling, and added tests verifying triggerRef behavior for numeric and Symbol keys on reactive sources.

Changes

Cohort / File(s) Summary
Tests
packages/reactivity/__tests__/ref.spec.ts
Added tests asserting triggerRef re-triggers effects exactly once for toRef created from reactive arrays using numeric keys (0 and [0]) and for toRef created from reactive objects using a Symbol key.
Reactivity implementation
packages/reactivity/src/ref.ts
Expanded toRef/toRefs typings to accept Record<PropertyKey, any> and `key?: string
Type tests
packages-private/dts-test/ref.test-d.ts
Added TypeScript assertions for toRef with reactive arrays and fixed-length tuples ensuring correct Ref value types for numeric string keys and tuple indices.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jh-leong

Poem

🐇 I hopped through keys both small and grand,
kept symbols safe, and numbers in hand.
triggerRef now twitches once, not more—
one tiny hop, and effects restore. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed All code changes directly address the core requirement: normalizing property keys in toRef to enable triggerRef to work with refs created from reactive arrays, fixing #12427 and #12431.
Out of Scope Changes check ✅ Passed All changes are scoped to the toRef property-key handling, including tests and type definitions; no unrelated modifications are present.
Title check ✅ Passed The title accurately summarizes the main changes: normalizing toRef property keys for dependency lookup and improving type definitions to support symbol and numeric keys.

✏️ 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.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 25, 2026

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 105 kB (+29 B) 39.7 kB (+9 B) 35.7 kB (+1 B)
vue.global.prod.js 163 kB (+29 B) 59.7 kB (+6 B) 53.1 kB (-16 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 48.2 kB 18.7 kB 17.2 kB
createApp 56.3 kB 21.8 kB 19.9 kB
createSSRApp 60.6 kB 23.5 kB 21.5 kB
defineCustomElement 62.5 kB 23.7 kB 21.6 kB
overall 70.7 kB 27.1 kB 24.7 kB

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 25, 2026

Open in StackBlitz

@vue/compiler-core

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

@vue/compiler-dom

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

@vue/compiler-sfc

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

@vue/compiler-ssr

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

@vue/reactivity

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

@vue/runtime-core

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

@vue/runtime-dom

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

@vue/server-renderer

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

@vue/shared

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

vue

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

@vue/compat

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

commit: b6e718b

@edison1105 edison1105 added ready to merge The PR is ready to be merged. scope: reactivity 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Mar 25, 2026
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.

🧹 Nitpick comments (1)
packages/reactivity/src/ref.ts (1)

377-377: Minor: Redundant String() call for already-stringified keys.

Since this._key is already a string for non-symbol keys (after line 370), the String() wrapper is only necessary for the symbol case. This is functionally correct but slightly redundant. The current approach is acceptable for clarity.

Optional: Avoid redundant String() call
-    if (!isArray(_object) || !isIntegerKey(String(this._key))) {
+    if (!isArray(_object) || isSymbol(this._key) || !isIntegerKey(this._key)) {

This makes the intent clearer: symbols are never integer keys, and for strings we can check directly.

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

In `@packages/reactivity/src/ref.ts` at line 377, The condition uses
String(this._key) redundantly because this._key is already a string for
non-symbol keys; replace the String(...) call with the raw key and, if you want
to be explicit about symbol keys, guard with typeof: use this._key directly in
isIntegerKey or use typeof this._key === 'symbol' ? false :
isIntegerKey(this._key) so the check becomes if (!isArray(_object) ||
!isIntegerKey(this._key)) (or the equivalent guarded form) referencing isArray,
isIntegerKey, _object and this._key.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/reactivity/src/ref.ts`:
- Line 377: The condition uses String(this._key) redundantly because this._key
is already a string for non-symbol keys; replace the String(...) call with the
raw key and, if you want to be explicit about symbol keys, guard with typeof:
use this._key directly in isIntegerKey or use typeof this._key === 'symbol' ?
false : isIntegerKey(this._key) so the check becomes if (!isArray(_object) ||
!isIntegerKey(this._key)) (or the equivalent guarded form) referencing isArray,
isIntegerKey, _object and this._key.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fc76e1b6-41f1-413d-b327-2e5bcae01687

📥 Commits

Reviewing files that changed from the base of the PR and between d61d803 and 1fb350d.

📒 Files selected for processing (2)
  • packages/reactivity/__tests__/ref.spec.ts
  • packages/reactivity/src/ref.ts

@edison1105 edison1105 changed the title fix(reactivity): coerce toRef property keys to strings fix(reactivity): normalize toRef property keys before dep lookup Mar 25, 2026
@edison1105 edison1105 changed the title fix(reactivity): normalize toRef property keys before dep lookup fix(reactivity): normalize toRef property keys before dep lookup + improve types Mar 25, 2026
@edison1105
Copy link
Copy Markdown
Member Author

/ecosystem-ci run

@vue-bot
Copy link
Copy Markdown
Contributor

vue-bot commented Mar 25, 2026

📝 Ran ecosystem CI: Open

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

@edison1105 edison1105 merged commit 1bb28d0 into vuejs:main Mar 25, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. ready to merge The PR is ready to be merged. scope: reactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

triggerRef fails when target is an array

2 participants