From 679f5c2189f1647480d8cd41d9d67b225ffd83d4 Mon Sep 17 00:00:00 2001 From: MinatoHikari <35342316+MinatoHikari@users.noreply.github.com> Date: Wed, 27 Apr 2022 21:25:24 +0800 Subject: [PATCH] fix(type): align watch types with vue3 (#927) --- src/apis/watch.ts | 48 ++++++++++++++++++--------- test-dts/watch.test-d.tsx | 39 ++++++++++++++++++---- test/v3/runtime-core/apiWatch.spec.ts | 2 +- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/apis/watch.ts b/src/apis/watch.ts index b3e0984c..00788d52 100644 --- a/src/apis/watch.ts +++ b/src/apis/watch.ts @@ -32,11 +32,7 @@ export type WatchCallback = ( onInvalidate: InvalidateCbRegistrator ) => any -type MapSources = { - [K in keyof T]: T[K] extends WatchSource ? V : never -} - -type MapOldSources = { +declare type MapSources = { [K in keyof T]: T[K] extends WatchSource ? Immediate extends true ? V | undefined @@ -44,6 +40,8 @@ type MapOldSources = { : never } +type MultiWatchSources = (WatchSource | object)[] + export interface WatchOptionsBase { flush?: FlushMode // onTrack?: ReactiveEffectOptions['onTrack']; @@ -204,8 +202,8 @@ function patchWatcherTeardown(watcher: VueWatcher, runCleanup: () => void) { function createWatcher( vm: ComponentInstance, - source: WatchSource | WatchSource[] | WatchEffect, - cb: WatchCallback | null, + source: WatchSource | WatchSource[] | WatchEffect, + cb: WatchCallback | null, options: WatchOptions ): () => void { if (__DEV__ && !cb) { @@ -416,27 +414,47 @@ export function watchSyncEffect(effect: WatchEffect) { return watchEffect(effect, { flush: 'sync' }) } -// overload #1: array of multiple sources + cb -// Readonly constraint helps the callback to correctly infer value types based -// on position in the source array. Otherwise the values will get a union type -// of all possible value types. +// overload #1 + #2 + #3: array of multiple sources + cb + +// overload #1: In readonly case the first overload must be spread tuple type. +// In otherwise members in tuple can not get the correct types. +export function watch< + T extends Readonly, + Immediate extends Readonly = false +>( + sources: [...T], + cb: WatchCallback, MapSources>, + options?: WatchOptions +): WatchStopHandle + +// overload #2: for not spread readonly tuple +export function watch< + T extends Readonly, + Immediate extends Readonly = false +>( + sources: T, + cb: WatchCallback, MapSources>, + options?: WatchOptions +): WatchStopHandle + +// overload #3: for not readonly multiSources export function watch< - T extends Readonly[]>, + T extends MultiWatchSources, Immediate extends Readonly = false >( sources: [...T], - cb: WatchCallback, MapOldSources>, + cb: WatchCallback, MapSources>, options?: WatchOptions ): WatchStopHandle -// overload #2: single source + cb +// overload #4: single source + cb export function watch = false>( source: WatchSource, cb: WatchCallback, options?: WatchOptions ): WatchStopHandle -// overload #3: watching reactive object w/ cb +// overload #5: watching reactive object w/ cb export function watch< T extends object, Immediate extends Readonly = false diff --git a/test-dts/watch.test-d.tsx b/test-dts/watch.test-d.tsx index 2811743c..581faa2a 100644 --- a/test-dts/watch.test-d.tsx +++ b/test-dts/watch.test-d.tsx @@ -10,17 +10,44 @@ watch(source, (value, oldValue) => { expectType(oldValue) }) -watch([source, source2, source3], (values, oldValues) => { - expectType<(string | number)[]>(values) - expectType<(string | number)[]>(oldValues) -}) +// spread array +watch( + [source, source2, source3], + ([source1, source2, source3], [oldSource1, oldSource2, oldSource3]) => { + expectType(source1) + expectType(source2) + expectType(source3) + expectType(oldSource1) + expectType(oldSource2) + expectType(oldSource3) + } +) // const array -watch([source, source2, source3], (values, oldValues) => { +watch([source, source2, source3] as const, (values, oldValues) => { expectType>(values) expectType>(oldValues) + expectType(values[0]) + expectType(values[1]) + expectType(values[2]) + expectType(oldValues[0]) + expectType(oldValues[1]) + expectType(oldValues[2]) }) +// const spread array +watch( + [source, source2, source3] as const, + ([source1, source2, source3], [oldSource1, oldSource2, oldSource3]) => { + expectType(source1) + expectType(source2) + expectType(source3) + expectType(oldSource1) + expectType(oldSource2) + expectType(oldSource3) + } +) + // immediate watcher's oldValue will be undefined on first run. watch( source, @@ -42,7 +69,7 @@ watch( // const array watch( - [source, source2, source3], + [source, source2, source3] as const, (values, oldValues) => { expectType>(values) expectType< diff --git a/test/v3/runtime-core/apiWatch.spec.ts b/test/v3/runtime-core/apiWatch.spec.ts index b6825e0f..57dc6335 100644 --- a/test/v3/runtime-core/apiWatch.spec.ts +++ b/test/v3/runtime-core/apiWatch.spec.ts @@ -143,7 +143,7 @@ describe('api: watch', () => { const status = ref(false) let dummy - watch([() => state.count, status], (vals, oldVals) => { + watch([() => state.count, status] as const, (vals, oldVals) => { dummy = [vals, oldVals] const [count] = vals const [, oldStatus] = oldVals