Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/reactivity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export {
traverse,
onWatcherCleanup,
WatchErrorCodes,
type MultiWatchSources,
type WatchOptions,
type WatchScheduler,
type WatchStopHandle,
Expand Down
61 changes: 60 additions & 1 deletion packages/reactivity/src/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
pauseTracking,
resetTracking,
} from './effect'
import { isReactive, isShallow } from './reactive'
import { type ReactiveMarker, isReactive, isShallow } from './reactive'
import { type Ref, isRef } from './ref'
import { getCurrentScope } from './effectScope'

Expand Down Expand Up @@ -117,6 +117,65 @@ export function onWatcherCleanup(
}
}

type MaybeUndefined<T, I> = I extends true ? T | undefined : T

export type MultiWatchSources = (WatchSource<unknown> | object)[]

type MapSources<T, Immediate> = {
[K in keyof T]: T[K] extends WatchSource<infer V>
? MaybeUndefined<V, Immediate>
: T[K] extends object
? MaybeUndefined<T[K], Immediate>
: never
}

// overload: simple effect
export function watch<Immediate extends Readonly<boolean> = false>(
source: WatchEffect,
cb?: null,
options?: WatchOptions<Immediate>,
): WatchHandle

// overload: single source + cb
export function watch<T, Immediate extends Readonly<boolean> = false>(
source: WatchSource<T>,
cb: WatchCallback<T, MaybeUndefined<T, Immediate>>,
options?: WatchOptions<Immediate>,
): WatchHandle

// overload: reactive array or tuple of multiple sources + cb
export function watch<
T extends Readonly<MultiWatchSources>,
Immediate extends Readonly<boolean> = false,
>(
sources: readonly [...T] | T,
cb: [T] extends [ReactiveMarker]
? WatchCallback<T, MaybeUndefined<T, Immediate>>
: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
options?: WatchOptions<Immediate>,
): WatchHandle

// overload: array of multiple sources + cb
export function watch<
T extends MultiWatchSources,
Immediate extends Readonly<boolean> = false,
>(
sources: [...T],
cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
options?: WatchOptions<Immediate>,
): WatchHandle

// overload: watching reactive object w/ cb
export function watch<
T extends object,
Immediate extends Readonly<boolean> = false,
>(
source: T,
cb: WatchCallback<T, MaybeUndefined<T, Immediate>>,
options?: WatchOptions<Immediate>,
): WatchHandle

// implementation
export function watch(
source: WatchSource | WatchSource[] | WatchEffect | object,
cb?: WatchCallback | null,
Expand Down
5 changes: 2 additions & 3 deletions packages/runtime-core/src/apiWatch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
type WatchOptions as BaseWatchOptions,
type DebuggerOptions,
type MultiWatchSources,
type ReactiveMarker,
type WatchCallback,
type WatchEffect,
Expand Down Expand Up @@ -81,8 +82,6 @@ export function watchSyncEffect(
)
}

export type MultiWatchSources = (WatchSource<unknown> | object)[]

// overload: single source + cb
export function watch<T, Immediate extends Readonly<boolean> = false>(
source: WatchSource<T>,
Expand Down Expand Up @@ -223,7 +222,7 @@ function doWatch(
}
}

const watchHandle = baseWatch(source, cb, baseWatchOptions)
const watchHandle = baseWatch(source, cb as any, baseWatchOptions)

if (__SSR__ && isInSSRComponentSetup) {
if (ssrCleanup) {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,9 @@ export type {
DebuggerEventExtraInfo,
Raw,
Reactive,
MultiWatchSources,
} from '@vue/reactivity'
export type {
MultiWatchSources,
WatchEffect,
WatchOptions,
WatchEffectOptions as WatchOptionsBase,
Expand Down
Loading