@@ -4,35 +4,53 @@ import { assert, logError, noopFn, warn } from '../utils';
4
4
import { defineComponentInstance } from '../helper' ;
5
5
import { getCurrentVM , getCurrentVue } from '../runtimeContext' ;
6
6
import { WatcherPreFlushQueueKey , WatcherPostFlushQueueKey } from '../symbols' ;
7
+ import { ComputedRef } from './computed' ;
7
8
8
- type CleanupRegistrator = ( invalidate : ( ) => void ) => void ;
9
+ export type WatchEffect = ( onInvalidate : InvalidateCbRegistrator ) => void ;
9
10
10
- type SimpleEffect = ( onCleanup : CleanupRegistrator ) => void ;
11
+ export type WatchSource < T = any > = Ref < T > | ComputedRef < T > | ( ( ) => T ) ;
11
12
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 ;
17
18
18
19
type 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 ;
20
21
} ;
21
22
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
+ } ;
23
30
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' ;
29
34
30
35
export interface VueWatcher {
31
36
lazy : boolean ;
32
37
get ( ) : any ;
33
38
teardown ( ) : void ;
34
39
}
35
40
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
+
36
54
let fallbackVM : ComponentInstance ;
37
55
38
56
function flushPreQueue ( this : any ) {
@@ -54,17 +72,28 @@ function installWatchEnv(vm: any) {
54
72
vm . $on ( 'hook:updated' , flushPostQueue ) ;
55
73
}
56
74
57
- function getWatcherOption ( options ?: Partial < WatcherOption > ) : WatcherOption {
75
+ function getWatcherOption ( options ?: Partial < WatchOptions > ) : WatchOptions {
58
76
return {
59
77
...{
60
- lazy : false ,
78
+ immediate : false ,
61
79
deep : false ,
62
80
flush : 'post' ,
63
81
} ,
64
82
...options ,
65
83
} ;
66
84
}
67
85
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
+
68
97
function getWatcherVM ( ) {
69
98
let vm = getCurrentVM ( ) ;
70
99
if ( ! vm ) {
@@ -141,14 +170,14 @@ function createVueWatcher(
141
170
142
171
function createWatcher (
143
172
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
147
176
) : ( ) => void {
148
177
const flushMode = options . flush ;
149
178
const isSync = flushMode === 'sync' ;
150
179
let cleanup : ( ( ) => void ) | null ;
151
- const registerCleanup : CleanupRegistrator = ( fn : ( ) => void ) => {
180
+ const registerCleanup : InvalidateCbRegistrator = ( fn : ( ) => void ) => {
152
181
cleanup = ( ) => {
153
182
try {
154
183
fn ( ) ;
@@ -180,10 +209,10 @@ function createWatcher(
180
209
181
210
// effect watch
182
211
if ( cb === null ) {
183
- const getter = ( ) => ( source as SimpleEffect ) ( registerCleanup ) ;
212
+ const getter = ( ) => ( source as WatchEffect ) ( registerCleanup ) ;
184
213
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 ,
187
216
sync : isSync ,
188
217
before : runCleanup ,
189
218
} ) ;
@@ -220,7 +249,7 @@ function createWatcher(
220
249
cb ( n , o , registerCleanup ) ;
221
250
} ;
222
251
let callback = createScheduler ( applyCb ) ;
223
- if ( ! options . lazy ) {
252
+ if ( options . immediate ) {
224
253
const originalCallbck = callback ;
225
254
// `shiftCallback` is used to handle the first sync effect run.
226
255
// The subsequent callbacks will redirect to `callback`.
@@ -235,7 +264,7 @@ function createWatcher(
235
264
236
265
// @ts -ignore: use undocumented option "sync"
237
266
const stop = vm . $watch ( getter , callback , {
238
- immediate : ! options . lazy ,
267
+ immediate : options . immediate ,
239
268
deep : options . deep ,
240
269
sync : isSync ,
241
270
} ) ;
@@ -246,38 +275,42 @@ function createWatcher(
246
275
} ;
247
276
}
248
277
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 ) ;
254
280
const vm = getWatcherVM ( ) ;
255
281
return createWatcher ( vm , effect , null , opts ) ;
256
282
}
257
283
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 >
266
289
) : 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
+ > (
268
299
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 >
271
302
) : 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
276
309
) : StopHandle {
277
- let callback : WatcherCallBack < unknown > | null = null ;
310
+ let callback : WatchCallback < unknown > | null = null ;
278
311
if ( typeof cb === 'function' ) {
279
312
// source watch
280
- callback = cb as WatcherCallBack < unknown > ;
313
+ callback = cb as WatchCallback < unknown > ;
281
314
} else {
282
315
// effect watch
283
316
if ( __DEV__ ) {
@@ -287,7 +320,7 @@ export function watch(
287
320
`supports \`watch(source, cb, options?) signature.`
288
321
) ;
289
322
}
290
- options = cb as Partial < WatcherOption > ;
323
+ options = cb as Partial < WatchOptions > ;
291
324
callback = null ;
292
325
}
293
326
0 commit comments