@@ -4,18 +4,24 @@ const { proxyStateMap, snapCache } = unstable_getInternalStates()
44const maybeProxify = ( x : any ) => ( typeof x === 'object' ? proxy ( { x } ) . x : x )
55const isProxy = ( x : any ) => proxyStateMap . has ( x )
66
7+ type RSetLike < T > = { has ( value : T ) : boolean }
8+
79type InternalProxySet < T > = Set < T > & {
810 data : T [ ]
9- toJSON : object
11+ toJSON : ( ) => object
1012 index : number
1113 epoch : number
12- intersection : ( other : Set < T > ) => Set < T >
13- union : ( other : Set < T > ) => Set < T >
14- difference : ( other : Set < T > ) => Set < T >
15- symmetricDifference : ( other : Set < T > ) => Set < T >
16- isSubsetOf : ( other : Set < T > ) => boolean
17- isSupersetOf : ( other : Set < T > ) => boolean
18- isDisjointFrom : ( other : Set < T > ) => boolean
14+ intersection < U > ( other : RSetLike < U > ) : Set < T & U >
15+ intersection ( other : Set < T > ) : Set < T >
16+ union < U > ( other : RSetLike < U > ) : Set < T | U >
17+ union ( other : Set < T > ) : Set < T >
18+ difference < U > ( other : RSetLike < U > ) : Set < T >
19+ difference ( other : Set < T > ) : Set < T >
20+ symmetricDifference < U > ( other : RSetLike < U > ) : Set < T | U >
21+ symmetricDifference ( other : Set < T > ) : Set < T >
22+ isSubsetOf ( other : RSetLike < T > ) : boolean
23+ isSupersetOf ( other : RSetLike < T > ) : boolean
24+ isDisjointFrom ( other : RSetLike < T > ) : boolean
1925}
2026
2127/**
@@ -55,6 +61,7 @@ export const isProxySet = (obj: object): boolean => {
5561 * set: proxySet()
5662 * })
5763 */
64+
5865export function proxySet < T > ( initialValues ?: Iterable < T > | null ) {
5966 const initialData : T [ ] = [ ]
6067 const indexMap = new Map < T , number > ( )
@@ -84,6 +91,98 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
8491 }
8592 }
8693
94+ const isIterable = ( o : unknown ) : o is Iterable < unknown > =>
95+ typeof o === 'object' && o !== null && Symbol . iterator in ( o as object )
96+
97+ const hasForEach = < U > (
98+ o : RSetLike < U > ,
99+ ) : o is RSetLike < U > & { forEach : ( cb : ( v : U ) => void ) => void } =>
100+ typeof ( o as { forEach ?: unknown } ) . forEach === 'function'
101+
102+ const asIterable = < U > ( other : RSetLike < U > | Set < U > ) : Iterable < U > => {
103+ if ( isIterable ( other ) ) return other as Iterable < U >
104+ if ( hasForEach ( other ) ) {
105+ const acc : U [ ] = [ ]
106+ other . forEach ( ( v ) => acc . push ( v ) )
107+ return acc
108+ }
109+ throw new TypeError ( 'Expected an iterable' )
110+ }
111+
112+ function intersectionImpl < T , U > (
113+ this : InternalProxySet < T > ,
114+ other : RSetLike < U > ,
115+ ) : Set < T & U >
116+ function intersectionImpl < T > ( this : InternalProxySet < T > , other : Set < T > ) : Set < T >
117+ function intersectionImpl < T > (
118+ this : InternalProxySet < T > ,
119+ other : RSetLike < unknown > | Set < T > ,
120+ ) : Set < unknown > {
121+ this . epoch // touch property for tracking
122+ const otherSet = proxySet ( asIterable ( other ) )
123+ const result = proxySet < T > ( )
124+ for ( const value of this . values ( ) ) {
125+ if ( otherSet . has ( value ) ) {
126+ result . add ( value )
127+ }
128+ }
129+ return proxySet ( result )
130+ }
131+
132+ function unionImpl < T , U > (
133+ this : InternalProxySet < T > ,
134+ other : RSetLike < U > ,
135+ ) : Set < T | U >
136+ function unionImpl < T > ( this : InternalProxySet < T > , other : Set < T > ) : Set < T >
137+ function unionImpl < T > (
138+ this : InternalProxySet < T > ,
139+ other : RSetLike < unknown > | Set < T > ,
140+ ) : Set < unknown > {
141+ this . epoch // touch property for tracking
142+ const otherSet = proxySet ( asIterable ( other ) )
143+ const result = proxySet < unknown > ( )
144+ for ( const v of this . values ( ) ) result . add ( v )
145+ for ( const v of otherSet . values ( ) ) result . add ( v )
146+ return proxySet ( result )
147+ }
148+
149+ function differenceImpl < T , U > (
150+ this : InternalProxySet < T > ,
151+ other : RSetLike < U > ,
152+ ) : Set < T >
153+ function differenceImpl < T > ( this : InternalProxySet < T > , other : Set < T > ) : Set < T >
154+ function differenceImpl < T > (
155+ this : InternalProxySet < T > ,
156+ other : RSetLike < unknown > | Set < T > ,
157+ ) : Set < T > {
158+ this . epoch // touch property for tracking
159+ const otherSet = proxySet ( asIterable ( other ) )
160+ const result = proxySet < T > ( )
161+ for ( const v of this . values ( ) ) if ( ! otherSet . has ( v ) ) result . add ( v )
162+ return proxySet ( result )
163+ }
164+
165+ function symmetricDifferenceImpl < T , U > (
166+ this : InternalProxySet < T > ,
167+ other : RSetLike < U > ,
168+ ) : Set < T | U >
169+ function symmetricDifferenceImpl < T > (
170+ this : InternalProxySet < T > ,
171+ other : Set < T > ,
172+ ) : Set < T >
173+ function symmetricDifferenceImpl < T > (
174+ this : InternalProxySet < T > ,
175+ other : RSetLike < unknown > | Set < T > ,
176+ ) : Set < unknown > {
177+ this . epoch // touch property for tracking
178+ const otherSet = proxySet ( asIterable ( other ) )
179+ const result = proxySet < unknown > ( )
180+ for ( const v of this . values ( ) ) if ( ! otherSet . has ( v ) ) result . add ( v )
181+ // (v as T) -> v is unknown from RSetLike<unknown>, but has() accepts T and safely handles type mismatches
182+ for ( const v of otherSet . values ( ) ) if ( ! this . has ( v as T ) ) result . add ( v )
183+ return proxySet ( result )
184+ }
185+
87186 const vObject : InternalProxySet < T > = {
88187 data : initialData ,
89188 index : initialIndex ,
@@ -135,7 +234,7 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
135234 this . epoch ++
136235 indexMap . clear ( )
137236 } ,
138- forEach ( cb ) {
237+ forEach ( cb : ( value : T , valueAgain : T , set : Set < T > ) => void ) {
139238 this . epoch // touch property for tracking
140239 const map = getMapForThis ( this )
141240 map . forEach ( ( index ) => {
@@ -170,76 +269,25 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
170269 get [ Symbol . toStringTag ] ( ) {
171270 return 'Set'
172271 } ,
173- intersection ( other : Set < T > ) : Set < T > {
272+ intersection : intersectionImpl ,
273+ union : unionImpl ,
274+ difference : differenceImpl ,
275+ symmetricDifference : symmetricDifferenceImpl ,
276+ isSubsetOf ( other : RSetLike < T > ) {
174277 this . epoch // touch property for tracking
175- const otherSet = proxySet < T > ( other )
176- const resultSet = proxySet < T > ( )
177- for ( const value of this . values ( ) ) {
178- if ( otherSet . has ( value ) ) {
179- resultSet . add ( value )
180- }
181- }
182- return proxySet ( resultSet )
183- } ,
184- union ( other : Set < T > ) {
185- this . epoch // touch property for tracking
186- const resultSet = proxySet < T > ( )
187- const otherSet = proxySet < T > ( other )
188- for ( const value of this . values ( ) ) {
189- resultSet . add ( value )
190- }
191- for ( const value of otherSet ) {
192- resultSet . add ( value )
193- }
194- return proxySet ( resultSet )
195- } ,
196- difference ( other : Set < T > ) {
197- this . epoch // touch property for tracking
198- const resultSet = proxySet < T > ( )
199- const otherSet = proxySet < T > ( other )
200- for ( const value of this . values ( ) ) {
201- if ( ! otherSet . has ( value ) ) {
202- resultSet . add ( value )
203- }
204- }
205- return proxySet ( resultSet )
206- } ,
207- symmetricDifference ( other : Set < T > ) {
208- this . epoch // touch property for tracking
209- const resultSet = proxySet < T > ( )
210- const otherSet = proxySet < T > ( other )
211- for ( const value of this . values ( ) ) {
212- if ( ! otherSet . has ( value ) ) {
213- resultSet . add ( value )
214- }
215- }
216- for ( const value of otherSet . values ( ) ) {
217- if ( ! this . has ( value ) ) {
218- resultSet . add ( value )
219- }
220- }
221- return proxySet ( resultSet )
222- } ,
223- isSubsetOf ( other : Set < T > ) {
224- this . epoch // touch property for tracking
225- const otherSet = proxySet < T > ( other )
226- return (
227- this . size <= other . size &&
228- [ ...this . values ( ) ] . every ( ( value ) => otherSet . has ( value ) )
229- )
278+ for ( const v of this . values ( ) ) if ( ! other . has ( v ) ) return false
279+ return true
230280 } ,
231- isSupersetOf ( other : Set < T > ) {
281+ isSupersetOf ( other : RSetLike < T > ) {
232282 this . epoch // touch property for tracking
233- const otherSet = proxySet < T > ( other )
234- return (
235- this . size >= other . size &&
236- [ ...otherSet ] . every ( ( value ) => this . has ( value ) )
237- )
283+ const it = asIterable ( other )
284+ for ( const v of it ) if ( ! this . has ( v ) ) return false
285+ return true
238286 } ,
239- isDisjointFrom ( other : Set < T > ) : boolean {
287+ isDisjointFrom ( other : RSetLike < T > ) {
240288 this . epoch // touch property for tracking
241- const otherSet = proxySet < T > ( other )
242- return [ ... this . values ( ) ] . every ( ( value ) => ! otherSet . has ( value ) )
289+ for ( const v of this . values ( ) ) if ( other . has ( v ) ) return false
290+ return true
243291 } ,
244292 }
245293
@@ -253,7 +301,7 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
253301 } )
254302 Object . seal ( proxiedObject )
255303
256- return proxiedObject as unknown as InternalProxySet < T > & {
304+ return proxiedObject as InternalProxySet < T > & {
257305 $$valtioSnapshot : Omit < InternalProxySet < T > , 'set' | 'delete' | 'clear' >
258306 }
259307}
0 commit comments