1- import type { Ref } from 'vue-demi'
1+ import { type Ref } from 'vue-demi'
22import type { ConfigurableFlushSync } from '../utils'
33import type { WatchPausableReturn } from '../watchPausable'
44import { pausableWatch } from '../watchPausable'
55
6- export interface SyncRefOptions < L , R = L > extends ConfigurableFlushSync {
6+ type Direction = 'ltr' | 'rtl' | 'both'
7+ type SpecificFieldPartial < T , K extends keyof T > = Partial < Pick < T , K > > & Omit < T , K >
8+ /**
9+ * A = B
10+ */
11+ type Equal < A , B > = A extends B ? ( B extends A ? true : false ) : false
12+
13+ /**
14+ * A ∩ B ≠ ∅
15+ */
16+ type IntersectButNotEqual < A , B > = Equal < A , B > extends true
17+ ? false
18+ : A & B extends never
19+ ? false
20+ : true
21+
22+ /**
23+ * A ⊆ B
24+ */
25+ type IncludeButNotEqual < A , B > = Equal < A , B > extends true
26+ ? false
27+ : A extends B
28+ ? true
29+ : false
30+
31+ /**
32+ * A ∩ B = ∅
33+ */
34+ type NotIntersect < A , B > = Equal < A , B > extends true
35+ ? false
36+ : A & B extends never
37+ ? true
38+ : false
39+
40+ // L = R
41+ interface EqualType <
42+ D extends Direction ,
43+ L ,
44+ R ,
45+ O extends keyof Transform < L , R > = D extends 'both' ? 'ltr' | 'rtl' : D ,
46+ > {
47+ transform ?: SpecificFieldPartial < Pick < Transform < L , R > , O > , O >
48+ }
49+
50+ type StrictIncludeMap < IncludeType extends 'LR' | 'RL' , D extends Exclude < Direction , 'both' > , L , R > = ( Equal < [ IncludeType , D ] , [ 'LR' , 'ltr' ] >
51+ & Equal < [ IncludeType , D ] , [ 'RL' , 'rtl' ] > ) extends true
52+ ? {
53+ transform ?: SpecificFieldPartial < Pick < Transform < L , R > , D > , D >
54+ } : {
55+ transform : Pick < Transform < L , R > , D >
56+ }
57+
58+ // L ⊆ R
59+ type StrictIncludeType < IncludeType extends 'LR' | 'RL' , D extends Direction , L , R > = D extends 'both'
60+ ? {
61+ transform : SpecificFieldPartial < Transform < L , R > , IncludeType extends 'LR' ? 'ltr' : 'rtl' >
62+ }
63+ : D extends Exclude < Direction , 'both' >
64+ ? StrictIncludeMap < IncludeType , D , L , R >
65+ : never
66+
67+ // L ∩ R ≠ ∅
68+ type IntersectButNotEqualType < D extends Direction , L , R > = D extends 'both'
69+ ? {
70+ transform : Transform < L , R >
71+ }
72+ : D extends Exclude < Direction , 'both' >
73+ ? {
74+ transform : Pick < Transform < L , R > , D >
75+ }
76+ : never
77+
78+ // L ∩ R = ∅
79+ type NotIntersectType < D extends Direction , L , R > = IntersectButNotEqualType < D , L , R >
80+ interface Transform < L , R > {
81+ ltr : ( left : L ) => R
82+ rtl : ( right : R ) => L
83+ }
84+
85+ type TransformType < D extends Direction , L , R > = Equal < L , R > extends true
86+ // L = R
87+ ? EqualType < D , L , R >
88+ : IncludeButNotEqual < L , R > extends true
89+ // L ⊆ R
90+ ? StrictIncludeType < 'LR' , D , L , R >
91+ : IncludeButNotEqual < R , L > extends true
92+ // R ⊆ L
93+ ? StrictIncludeType < 'RL' , D , L , R >
94+ : IntersectButNotEqual < L , R > extends true
95+ // L ∩ R ≠ ∅
96+ ? IntersectButNotEqualType < D , L , R >
97+ : NotIntersect < L , R > extends true
98+ // L ∩ R = ∅
99+ ? NotIntersectType < D , L , R >
100+ : never
101+
102+ export type SyncRefOptions < L , R , D extends Direction > = ConfigurableFlushSync & {
7103 /**
8104 * Watch deeply
9105 *
@@ -22,36 +118,41 @@ export interface SyncRefOptions<L, R = L> extends ConfigurableFlushSync {
22118 *
23119 * @default 'both'
24120 */
25- direction ?: 'ltr' | 'rtl' | 'both'
121+ direction ?: D
26122
27- /**
28- * Custom transform function
29- */
30- transform ?: {
31- ltr ?: ( left : L ) => R
32- rtl ?: ( right : R ) => L
33- }
34- }
123+ } & TransformType < D , L , R >
35124
36125/**
37126 * Two-way refs synchronization.
38- *
127+ * From the set theory perspective to restrict the option's type
128+ * Check in the following order:
129+ * 1. L = R
130+ * 2. L ∩ R ≠ ∅
131+ * 3. L ⊆ R
132+ * 4. L ∩ R = ∅
39133 * @param left
40134 * @param right
135+ * @param [options?]
41136 */
42- export function syncRef < L , R = L > ( left : Ref < L > , right : Ref < R > , options : SyncRefOptions < L , R > = { } ) {
137+ export function syncRef < L , R , D extends Direction > (
138+ left : Ref < L > ,
139+ right : Ref < R > ,
140+ ...[ options ] : Equal < L , R > extends true
141+ ? [ options ?: SyncRefOptions < L , R , D > ]
142+ : [ options : SyncRefOptions < L , R , D > ]
143+ ) {
43144 const {
44145 flush = 'sync' ,
45146 deep = false ,
46147 immediate = true ,
47148 direction = 'both' ,
48149 transform = { } ,
49- } = options
150+ } = options || { }
50151
51152 const watchers : WatchPausableReturn [ ] = [ ]
52153
53- const transformLTR = transform . ltr ?? ( v => v )
54- const transformRTL = transform . rtl ?? ( v => v )
154+ const transformLTR = ( 'ltr' in transform && transform . ltr ) || ( v => v )
155+ const transformRTL = ( 'rtl' in transform && transform . rtl ) || ( v => v )
55156
56157 if ( direction === 'both' || direction === 'ltr' ) {
57158 watchers . push ( pausableWatch (
0 commit comments