Skip to content

Commit f05bc4b

Browse files
authored
refactor(utils): changes to proxySet for new typescript impl (#1175)
* changes to proxyset for new typescript impl * updated proxyset and tsconfig to 2024 * removed inconsistent underscore and added reason comment * added context to comment * added tracking comments back and change hasIterator to isIterable
1 parent ecc5134 commit f05bc4b

2 files changed

Lines changed: 124 additions & 76 deletions

File tree

src/vanilla/utils/proxySet.ts

Lines changed: 123 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@ const { proxyStateMap, snapCache } = unstable_getInternalStates()
44
const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x)
55
const isProxy = (x: any) => proxyStateMap.has(x)
66

7+
type RSetLike<T> = { has(value: T): boolean }
8+
79
type 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+
5865
export 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
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"target": "es2021" /* FIXME we want to make it esnext */,
3+
"target": "esnext",
44
"strict": true,
55
"jsx": "react-jsx",
66
"esModuleInterop": true,

0 commit comments

Comments
 (0)