onTrigger?: ReactiveEffectOptions['onTrigger']
}
-type WatcherSource<T> = Ref<T> | (() => T)
+type StopHandle = () => void
+
+type WatcherSource<T = any> = Ref<T> | (() => T)
+
+type MapSources<T> = {
+ [K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never
+}
+
+type CleanupRegistrator = (invalidate: () => void) => void
+
+type SimpleEffect = (onCleanup: CleanupRegistrator) => void
const invoke = (fn: Function) => fn()
+export function watch(effect: SimpleEffect, options?: WatchOptions): StopHandle
+
export function watch<T>(
- source: WatcherSource<T> | WatcherSource<T>[],
- cb?: <V extends T>(
- newValue: V,
- oldValue: V,
- onInvalidate: (fn: () => void) => void
- ) => any | void,
- { lazy, flush, deep, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
-): () => void {
+ source: WatcherSource<T>,
+ cb: (newValue: T, oldValue: T, onCleanup: CleanupRegistrator) => any,
+ options?: WatchOptions
+): StopHandle
+
+export function watch<T extends WatcherSource<unknown>[]>(
+ sources: T,
+ cb: (
+ newValues: MapSources<T>,
+ oldValues: MapSources<T>,
+ onCleanup: CleanupRegistrator
+ ) => any,
+ options?: WatchOptions
+): StopHandle
+
+// implementation
+export function watch(
+ effectOrSource:
+ | WatcherSource<unknown>
+ | WatcherSource<unknown>[]
+ | SimpleEffect,
+ effectOrOptions?:
+ | ((value: any, oldValue: any, onCleanup: CleanupRegistrator) => any)
+ | WatchOptions,
+ options?: WatchOptions
+): StopHandle {
+ if (typeof effectOrOptions === 'function') {
+ // effect callback as 2nd argument - this is a source watcher
+ return doWatch(effectOrSource, effectOrOptions, options)
+ } else {
+ // 2nd argument is either missing or an options object
+ // - this is a simple effect watcher
+ return doWatch(effectOrSource, null, effectOrOptions)
+ }
+}
+
+function doWatch(
+ source: WatcherSource | WatcherSource[] | SimpleEffect,
+ cb:
+ | ((newValue: any, oldValue: any, onCleanup: CleanupRegistrator) => any)
+ | null,
+ { lazy, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
+): StopHandle {
const scheduler =
flush === 'sync' ? invoke : flush === 'pre' ? queueJob : queuePostFlushCb
? () => source.map(s => (isRef(s) ? s.value : s()))
: isRef(source)
? () => source.value
- : source
+ : () => source(registerCleanup)
const getter = deep ? () => traverse(baseGetter()) : baseGetter
let cleanup: any
- const registerCleanup = (fn: () => void) => {
+ const registerCleanup: CleanupRegistrator = (fn: () => void) => {
// TODO wrap the cleanup fn for error handling
cleanup = runner.onStop = fn
}