@@ -4,35 +4,53 @@ import { assert, logError, noopFn, warn } from '../utils';
44import { defineComponentInstance } from '../helper' ;
55import { getCurrentVM , getCurrentVue } from '../runtimeContext' ;
66import { WatcherPreFlushQueueKey , WatcherPostFlushQueueKey } from '../symbols' ;
7+ import { ComputedRef } from './computed' ;
78
8- type CleanupRegistrator = ( invalidate : ( ) => void ) => void ;
9+ export type WatchEffect = ( onInvalidate : InvalidateCbRegistrator ) => void ;
910
10- type SimpleEffect = ( onCleanup : CleanupRegistrator ) => void ;
11+ export type WatchSource < T = any > = Ref < T > | ComputedRef < T > | ( ( ) => T ) ;
1112
12- type StopHandle = ( ) => void ;
13-
14- type WatcherCallBack < T > = ( newVal : T , oldVal : T , onCleanup : CleanupRegistrator ) => void ;
15-
16- type WatcherSource < T > = Ref < T > | ( ( ) => T ) ;
13+ export type WatchCallback < V = any , OV = any > = (
14+ value : V ,
15+ oldValue : OV ,
16+ onInvalidate : InvalidateCbRegistrator
17+ ) => any ;
1718
1819type MapSources < T > = {
19- [ K in keyof T ] : T [ K ] extends WatcherSource < infer V > ? V : never ;
20+ [ K in keyof T ] : T [ K ] extends WatchSource < infer V > ? V : never ;
2021} ;
2122
22- type FlushMode = 'pre' | 'post' | 'sync' ;
23+ type MapOldSources < T , Immediate > = {
24+ [ K in keyof T ] : T [ K ] extends WatchSource < infer V >
25+ ? Immediate extends true
26+ ? ( V | undefined )
27+ : V
28+ : never ;
29+ } ;
2330
24- interface WatcherOption {
25- lazy : boolean ; // whether or not to delay callcack invoking
26- deep : boolean ;
27- flush : FlushMode ;
28- }
31+ type InvalidateCbRegistrator = ( cb : ( ) => void ) => void ;
32+
33+ type FlushMode = 'pre' | 'post' | 'sync' ;
2934
3035export interface VueWatcher {
3136 lazy : boolean ;
3237 get ( ) : any ;
3338 teardown ( ) : void ;
3439}
3540
41+ export interface BaseWatchOptions {
42+ flush ?: FlushMode ;
43+ // onTrack?: ReactiveEffectOptions['onTrack'];
44+ // onTrigger?: ReactiveEffectOptions['onTrigger'];
45+ }
46+
47+ export interface WatchOptions < Immediate = boolean > extends BaseWatchOptions {
48+ immediate ?: Immediate ;
49+ deep ?: boolean ;
50+ }
51+
52+ export type StopHandle = ( ) => void ;
53+
3654let fallbackVM : ComponentInstance ;
3755
3856function flushPreQueue ( this : any ) {
@@ -54,17 +72,28 @@ function installWatchEnv(vm: any) {
5472 vm . $on ( 'hook:updated' , flushPostQueue ) ;
5573}
5674
57- function getWatcherOption ( options ?: Partial < WatcherOption > ) : WatcherOption {
75+ function getWatcherOption ( options ?: Partial < WatchOptions > ) : WatchOptions {
5876 return {
5977 ...{
60- lazy : false ,
78+ immediate : false ,
6179 deep : false ,
6280 flush : 'post' ,
6381 } ,
6482 ...options ,
6583 } ;
6684}
6785
86+ function getWatchEffectOption ( options ?: Partial < WatchOptions > ) : WatchOptions {
87+ return {
88+ ...{
89+ immediate : true ,
90+ deep : false ,
91+ flush : 'sync' ,
92+ } ,
93+ ...options ,
94+ } ;
95+ }
96+
6897function getWatcherVM ( ) {
6998 let vm = getCurrentVM ( ) ;
7099 if ( ! vm ) {
@@ -141,14 +170,14 @@ function createVueWatcher(
141170
142171function createWatcher (
143172 vm : ComponentInstance ,
144- source : WatcherSource < unknown > | WatcherSource < unknown > [ ] | SimpleEffect ,
145- cb : WatcherCallBack < any > | null ,
146- options : WatcherOption
173+ source : WatchSource < unknown > | WatchSource < unknown > [ ] | WatchEffect ,
174+ cb : WatchCallback < any > | null ,
175+ options : WatchOptions
147176) : ( ) => void {
148177 const flushMode = options . flush ;
149178 const isSync = flushMode === 'sync' ;
150179 let cleanup : ( ( ) => void ) | null ;
151- const registerCleanup : CleanupRegistrator = ( fn : ( ) => void ) => {
180+ const registerCleanup : InvalidateCbRegistrator = ( fn : ( ) => void ) => {
152181 cleanup = ( ) => {
153182 try {
154183 fn ( ) ;
@@ -180,10 +209,10 @@ function createWatcher(
180209
181210 // effect watch
182211 if ( cb === null ) {
183- const getter = ( ) => ( source as SimpleEffect ) ( registerCleanup ) ;
212+ const getter = ( ) => ( source as WatchEffect ) ( registerCleanup ) ;
184213 const watcher = createVueWatcher ( vm , getter , noopFn , {
185- noRun : true , // take control the initial gettet invoking
186- deep : options . deep ,
214+ noRun : true , // take control the initial getter invoking
215+ deep : options . deep || false ,
187216 sync : isSync ,
188217 before : runCleanup ,
189218 } ) ;
@@ -220,7 +249,7 @@ function createWatcher(
220249 cb ( n , o , registerCleanup ) ;
221250 } ;
222251 let callback = createScheduler ( applyCb ) ;
223- if ( ! options . lazy ) {
252+ if ( options . immediate ) {
224253 const originalCallbck = callback ;
225254 // `shiftCallback` is used to handle the first sync effect run.
226255 // The subsequent callbacks will redirect to `callback`.
@@ -235,7 +264,7 @@ function createWatcher(
235264
236265 // @ts -ignore: use undocumented option "sync"
237266 const stop = vm . $watch ( getter , callback , {
238- immediate : ! options . lazy ,
267+ immediate : options . immediate ,
239268 deep : options . deep ,
240269 sync : isSync ,
241270 } ) ;
@@ -246,38 +275,42 @@ function createWatcher(
246275 } ;
247276}
248277
249- export function watchEffect (
250- effect : SimpleEffect ,
251- options ?: Omit < Partial < WatcherOption > , 'lazy' >
252- ) : StopHandle {
253- const opts = getWatcherOption ( options ) ;
278+ export function watchEffect ( effect : WatchEffect , options ?: BaseWatchOptions ) : StopHandle {
279+ const opts = getWatchEffectOption ( options ) ;
254280 const vm = getWatcherVM ( ) ;
255281 return createWatcher ( vm , effect , null , opts ) ;
256282}
257283
258- export function watch < T = any > (
259- source : SimpleEffect ,
260- options ?: Omit < Partial < WatcherOption > , 'lazy' >
261- ) : StopHandle ;
262- export function watch < T = any > (
263- source : WatcherSource < T > ,
264- cb : WatcherCallBack < T > ,
265- options ?: Partial < WatcherOption >
284+ // overload #1: single source + cb
285+ export function watch < T , Immediate extends Readonly < boolean > = false > (
286+ source : WatchSource < T > ,
287+ cb : WatchCallback < T , Immediate extends true ? ( T | undefined ) : T > ,
288+ options ?: WatchOptions < Immediate >
266289) : StopHandle ;
267- export function watch < T extends WatcherSource < unknown > [ ] > (
290+
291+ // overload #2: array of multiple sources + cb
292+ // Readonly constraint helps the callback to correctly infer value types based
293+ // on position in the source array. Otherwise the values will get a union type
294+ // of all possible value types.
295+ export function watch <
296+ T extends Readonly < WatchSource < unknown > [ ] > ,
297+ Immediate extends Readonly < boolean > = false
298+ > (
268299 sources : T ,
269- cb : ( newValues : MapSources < T > , oldValues : MapSources < T > , onCleanup : CleanupRegistrator ) => any ,
270- options ?: Partial < WatcherOption >
300+ cb : WatchCallback < MapSources < T > , MapOldSources < T , Immediate > > ,
301+ options ?: WatchOptions < Immediate >
271302) : StopHandle ;
272- export function watch (
273- source : WatcherSource < unknown > | WatcherSource < unknown > [ ] | SimpleEffect ,
274- cb ?: Partial < WatcherOption > | WatcherCallBack < any > ,
275- options ?: Partial < WatcherOption >
303+
304+ // implementation
305+ export function watch < T = any > (
306+ source : WatchSource < T > | WatchSource < T > [ ] ,
307+ cb : WatchCallback < T > ,
308+ options ?: WatchOptions
276309) : StopHandle {
277- let callback : WatcherCallBack < unknown > | null = null ;
310+ let callback : WatchCallback < unknown > | null = null ;
278311 if ( typeof cb === 'function' ) {
279312 // source watch
280- callback = cb as WatcherCallBack < unknown > ;
313+ callback = cb as WatchCallback < unknown > ;
281314 } else {
282315 // effect watch
283316 if ( __DEV__ ) {
@@ -287,7 +320,7 @@ export function watch(
287320 `supports \`watch(source, cb, options?) signature.`
288321 ) ;
289322 }
290- options = cb as Partial < WatcherOption > ;
323+ options = cb as Partial < WatchOptions > ;
291324 callback = null ;
292325 }
293326
0 commit comments