Skip to content

Conversation

@ArthurDarkstone
Copy link
Contributor

@ArthurDarkstone ArthurDarkstone commented Sep 23, 2025

removed some any types and used the correct type to ensure type safety

by the way, Is this WatchSource not in line with the expected design? The watch api is not directly using the type WatchSource

Summary by CodeRabbit

  • Refactor
    • Tightened internal typings around watcher and path-based APIs for clearer, safer public behavior.
  • Developer Experience
    • Improved editor autocompletion, type inference, and dev-time diagnostics when creating watches and referencing paths.
  • Chores
    • Removed loose casts and aligned internal definitions for more consistent development tooling.

Note: No runtime behavior changes; impact is limited to TypeScript/development-time ergonomics.

@coderabbitai
Copy link

coderabbitai bot commented Sep 23, 2025

Walkthrough

Tightens TypeScript typings in watcher-related code: narrows dev-only option casts, changes createPathGetter to accept a ComponentPublicInstance and return a union type, and makes dynamic property/path access use keyof and non-null assertions for safer typing.

Changes

Cohort / File(s) Summary
Watcher core typings & signature
packages/runtime-core/src/apiWatch.ts
- Cast dev-only options to WatchEffectOptions for watchPostEffect/watchSyncEffect.
- Change createPathGetter(ctx: any, path: string)createPathGetter(ctx: ComponentPublicInstance, path: string): () => WatchSource | WatchSource[] | WatchEffect | object and update inner getter return type from () => any to the union type.
- Replace publicThis/this.proxy casts with ComponentPublicInstance usage, add non-null assertions, and use keyof casts for dynamic property access.
Component options — dynamic property access
packages/runtime-core/src/componentOptions.ts
- Replace (publicThis as any)[key] with publicThis[key as keyof typeof publicThis] to perform type-safe dynamic property access while preserving runtime behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitch my ears at types anew,
Paths snugly fitted, tidy too.
Watchers hum with stricter tune,
Safer hops beneath the moon.
I nibble bugs and hop away—what a view! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 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 summarizes the primary change: improving type safety in watch-related functions including instanceWatch, which matches the file-level changes in runtime-core described in the summary. It uses a conventional commit-style prefix ("chore(types):") and avoids unnecessary detail while remaining specific. The phrasing is appropriate for scanable history and review.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link

@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.

Actionable comments posted: 0

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/apiWatch.ts (1)

266-275: Fix createPathGetter typing: return a WatchSource (function), not the source union.

createPathGetter returns a getter function. The current signature/inner return type suggests it returns the “source union” or a WatchEffect, which is inaccurate and harms type inference. Return a WatchSource<unknown> (i.e., () => unknown) and keep dynamic indexing simple.

-export function createPathGetter(ctx: ComponentPublicInstance , path: string): WatchSource | WatchSource[] | WatchEffect | object {
-  const segments = path.split('.')
-  return (): WatchSource | WatchSource[] | WatchEffect | object => {
-    let cur = ctx
-    for (let i = 0; i < segments.length && cur; i++) {
-      cur = cur[segments[i] as keyof typeof cur]
-    }
-    return cur
-  }
-}
+export function createPathGetter(
+  ctx: ComponentPublicInstance,
+  path: string,
+): WatchSource<unknown> {
+  const segments = path.split('.')
+  return (): unknown => {
+    let cur: any = ctx
+    for (let i = 0; i < segments.length && cur; i++) {
+      cur = cur[segments[i]]
+    }
+    return cur
+  }
+}
🧹 Nitpick comments (2)
packages/runtime-core/src/apiWatch.ts (2)

24-24: Use type-only, internal import to avoid runtime cycles.

Import ComponentPublicInstance as a type and from the local module to prevent accidental runtime imports and potential circular deps.

-import { ComponentPublicInstance } from '@vue/runtime-core'
+import type { ComponentPublicInstance } from './componentPublicInstance'

247-253: Avoid scattered non-null assertions; simplify dynamic key access.

Bind proxy once with a single non-null assertion and simplify the string-key access. This reduces noise and keeps intent clear.

-  const publicThis = this.proxy
+  const publicThis = this.proxy!
   const getter = isString(source)
     ? source.includes('.')
-      ? createPathGetter(publicThis!, source)
-      : () => publicThis![source as keyof typeof publicThis]
+      ? createPathGetter(publicThis, source)
+      : () => (publicThis as any)[source as string]
     : source.bind(publicThis, publicThis)

Please confirm there’s no path where this.proxy can be null when $watch is invoked (e.g., very early lifecycle or after unmount). If there is, we should guard and return a stopped handle.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b555f02 and e4d74e9.

📒 Files selected for processing (1)
  • packages/runtime-core/src/apiWatch.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/runtime-core/src/apiWatch.ts (4)
packages/shared/src/general.ts (2)
  • extend (24-24)
  • isString (51-51)
packages/runtime-core/src/index.ts (4)
  • WatchEffectOptions (232-232)
  • ComponentPublicInstance (299-299)
  • WatchSource (234-234)
  • WatchEffect (230-230)
packages/runtime-core/src/componentPublicInstance.ts (1)
  • ComponentPublicInstance (292-345)
packages/reactivity/src/watch.ts (2)
  • WatchSource (39-39)
  • WatchEffect (37-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Redirect rules
  • GitHub Check: Header rules
  • GitHub Check: Pages changed
🔇 Additional comments (2)
packages/runtime-core/src/apiWatch.ts (2)

70-72: LGTM: dev-only merge for post-flush.

Merging DebuggerOptions only in dev while forcing { flush: 'post' } in prod is correct since debug hooks are no-ops in prod.


81-83: LGTM: dev-only merge for sync-flush.

Same rationale as above; looks good.

@edison1105 edison1105 changed the title chore(apiWatch): improve type safety in watch functions and instanceWatch chore(types): improve type safety in watch functions and instanceWatch Sep 23, 2025
@github-actions
Copy link

github-actions bot commented Sep 23, 2025

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 101 kB (-79 B) 38.5 kB (-30 B) 34.6 kB (-25 B)
vue.global.prod.js 159 kB (-79 B) 58.6 kB (-30 B) 52.1 kB (-24 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 46.6 kB (-43 B) 18.2 kB (-14 B) 16.7 kB (+1 B)
createApp 54.6 kB (-43 B) 21.3 kB (-14 B) 19.4 kB (-13 B)
createSSRApp 58.9 kB (-43 B) 23 kB (-16 B) 21 kB (-11 B)
defineCustomElement 59.6 kB (-43 B) 22.8 kB (-15 B) 20.9 kB (-19 B)
overall 68.8 kB (-43 B) 26.4 kB (-18 B) 24.2 kB (-4 B)

@pkg-pr-new
Copy link

pkg-pr-new bot commented Sep 23, 2025

Open in StackBlitz

@vue/compiler-core

npm i https://pkg.pr.new/@vue/compiler-core@13918

@vue/compiler-dom

npm i https://pkg.pr.new/@vue/compiler-dom@13918

@vue/compiler-sfc

npm i https://pkg.pr.new/@vue/compiler-sfc@13918

@vue/compiler-ssr

npm i https://pkg.pr.new/@vue/compiler-ssr@13918

@vue/reactivity

npm i https://pkg.pr.new/@vue/reactivity@13918

@vue/runtime-core

npm i https://pkg.pr.new/@vue/runtime-core@13918

@vue/runtime-dom

npm i https://pkg.pr.new/@vue/runtime-dom@13918

@vue/server-renderer

npm i https://pkg.pr.new/@vue/server-renderer@13918

@vue/shared

npm i https://pkg.pr.new/@vue/shared@13918

vue

npm i https://pkg.pr.new/vue@13918

@vue/compat

npm i https://pkg.pr.new/@vue/compat@13918

commit: e2bfeec

@edison1105
Copy link
Member

Thanks for the PR.
Could you please fix the failing test?

@edison1105 edison1105 added 🧹 p1-chore Priority 1: this doesn't change code behavior. wait changes labels Sep 23, 2025
@ArthurDarkstone
Copy link
Contributor Author

Thanks for the PR. Could you please fix the failing test?

sry, there was a return type error, fixed it !

@edison1105 edison1105 added ready to merge The PR is ready to be merged. and removed wait changes labels Sep 24, 2025
@edison1105
Copy link
Member

/ecosystem-ci run

@vue-bot
Copy link
Contributor

vue-bot commented Sep 24, 2025

📝 Ran ecosystem CI: Open

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

Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/runtime-core/src/apiWatch.ts (2)

251-256: Tighten non-null handling and remove repeated assertions.
Initialize a narrowed variable once and (optionally) guard in dev to avoid surprising NPEs if called too early.

Apply this diff:

-  const publicThis = this.proxy
+  const publicThis = this.proxy!
+  if (__DEV__ && !this.proxy) {
+    warn(`instanceWatch called on an instance without proxy`)
+  }
   const getter = isString(source)
     ? source.includes('.')
-      ? createPathGetter(publicThis!, source)
-      : () => publicThis![source as keyof typeof publicThis]
+      ? createPathGetter(publicThis, source)
+      : () => publicThis[source as keyof typeof publicThis]
     : source.bind(publicThis, publicThis)

270-282: Return type of the getter should be the observed value (unknown), not “watchables”.
createPathGetter returns a value read from the instance (e.g., number/string/object), not a WatchSource/WatchEffect/array. Using that union conflates “source shape” with “returned value” and can leak into inference. Prefer unknown for the getter’s return type. Also, using keyof typeof cur here is brittle; indexing via a structural type is simpler and clearer.

Apply this diff:

-export function createPathGetter(
-  ctx: ComponentPublicInstance,
-  path: string,
-): () => WatchSource | WatchSource[] | WatchEffect | object {
+export function createPathGetter(
+  ctx: ComponentPublicInstance,
+  path: string,
+): () => unknown {
   const segments = path.split('.')
-  return (): WatchSource | WatchSource[] | WatchEffect | object => {
+  return (): unknown => {
     let cur = ctx
     for (let i = 0; i < segments.length && cur; i++) {
-      cur = cur[segments[i] as keyof typeof cur]
+      cur = (cur as Record<string, unknown>)[segments[i] as string]
     }
     return cur
   }
 }

Please run the repo typecheck and a local ecosystem CI slice to ensure no inference regressions around options-based watchers that rely on createPathGetter.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2bfeec and c9e48cd.

📒 Files selected for processing (1)
  • packages/runtime-core/src/apiWatch.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/runtime-core/src/apiWatch.ts (3)
packages/shared/src/general.ts (2)
  • extend (24-24)
  • isString (51-51)
packages/runtime-core/src/componentPublicInstance.ts (1)
  • ComponentPublicInstance (292-345)
packages/reactivity/src/watch.ts (2)
  • WatchSource (39-39)
  • WatchEffect (37-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Redirect rules
  • GitHub Check: Header rules
  • GitHub Check: Pages changed
🔇 Additional comments (3)
packages/runtime-core/src/apiWatch.ts (3)

24-24: Good: importing the concrete public instance type enhances safety.
Switching to a precise type for ctx is appropriate for the later path getter typing.


70-73: Dev-only merge is fine; confirm dropping DebuggerOptions in prod is intentional.
In prod, any DebuggerOptions passed are ignored. That aligns with dev-only behavior of onTrack/onTrigger, but please confirm this is desired.


83-86: Same note for sync variant.
Prod build ignores DebuggerOptions; ensure this matches expectations.

@edison1105 edison1105 merged commit fda47ac into vuejs:main Sep 24, 2025
12 of 13 checks passed
yangmingshan added a commit to vue-mini/vue-mini that referenced this pull request Oct 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🧹 p1-chore Priority 1: this doesn't change code behavior. ready to merge The PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants