Skip to content

Commit 427ded7

Browse files
committed
changes to proxyset for new typescript impl
1 parent ecc5134 commit 427ded7

File tree

2 files changed

+135
-91
lines changed

2 files changed

+135
-91
lines changed

src/vanilla/utils/proxySet.ts

Lines changed: 116 additions & 84 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,82 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
8491
}
8592
}
8693

94+
const hasIterator = (o: unknown): o is Iterable<unknown> =>
95+
typeof o === 'object' && o !== null && Symbol.iterator in (o as object)
96+
97+
const hasForEach = <U>(o: RSetLike<U>): o is RSetLike<U> & { forEach: (cb: (v: U) => void) => void } =>
98+
typeof (o as { forEach?: unknown }).forEach === 'function'
99+
100+
const asIterable = <U>(other: RSetLike<U> | Set<U>): Iterable<U> => {
101+
if (hasIterator(other)) return other as Iterable<U>
102+
if (hasForEach(other)) {
103+
const acc: U[] = []
104+
other.forEach((v) => acc.push(v))
105+
return acc
106+
}
107+
throw new TypeError('Expected an iterable')
108+
}
109+
110+
function intersectionImpl<T, U>(this: InternalProxySet<T>, other: RSetLike<U>): Set<T & U>
111+
function intersectionImpl<T>(this: InternalProxySet<T>, other: Set<T>): Set<T>
112+
function intersectionImpl<T>(
113+
this: InternalProxySet<T>,
114+
other: RSetLike<unknown> | Set<T>
115+
): Set<unknown> {
116+
this.epoch
117+
const otherSet = proxySet(asIterable(other))
118+
const result = proxySet<T>()
119+
for (const value of this.values()) {
120+
if (otherSet.has(value)) {
121+
result.add(value)
122+
}
123+
}
124+
return proxySet(result)
125+
}
126+
127+
function unionImpl<T, U>(this: InternalProxySet<T>, other: RSetLike<U>): Set<T | U>
128+
function unionImpl<T>(this: InternalProxySet<T>, other: Set<T>): Set<T>
129+
function unionImpl<T>(
130+
this: InternalProxySet<T>,
131+
other: RSetLike<unknown> | Set<T>
132+
): Set<unknown> {
133+
this.epoch
134+
const otherSet = proxySet(asIterable(other))
135+
const result = proxySet<unknown>()
136+
for (const v of this.values()) result.add(v)
137+
for (const v of otherSet.values()) result.add(v)
138+
return proxySet(result)
139+
}
140+
141+
function differenceImpl<T, U>(this: InternalProxySet<T>, _other: RSetLike<U>): Set<T>
142+
function differenceImpl<T>(this: InternalProxySet<T>, other: Set<T>): Set<T>
143+
function differenceImpl<T>(
144+
this: InternalProxySet<T>,
145+
other: RSetLike<unknown> | Set<T>
146+
): Set<T> {
147+
this.epoch
148+
const otherSet = proxySet(asIterable(other))
149+
const result = proxySet<T>()
150+
for (const v of this.values()) if (!otherSet.has(v)) result.add(v)
151+
return proxySet(result)
152+
}
153+
154+
function symmetricDifferenceImpl<T, U>(this: InternalProxySet<T>, other: RSetLike<U>): Set<T | U>
155+
function symmetricDifferenceImpl<T>(this: InternalProxySet<T>, other: Set<T>): Set<T>
156+
function symmetricDifferenceImpl<T>(
157+
this: InternalProxySet<T>,
158+
other: RSetLike<unknown> | Set<T>
159+
): Set<unknown> {
160+
this.epoch
161+
const otherSet = proxySet(asIterable(other))
162+
const result = proxySet<unknown>()
163+
for (const v of this.values()) if (!otherSet.has(v)) result.add(v)
164+
for (const v of otherSet.values()) if (!this.has(v as T)) result.add(v)
165+
return proxySet(result)
166+
}
167+
168+
169+
87170
const vObject: InternalProxySet<T> = {
88171
data: initialData,
89172
index: initialIndex,
@@ -97,7 +180,7 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
97180
has(value: T) {
98181
const map = getMapForThis(this)
99182
const v = maybeProxify(value)
100-
this.epoch // touch property for tracking
183+
this.epoch
101184
return map.has(v)
102185
},
103186
add(value: T) {
@@ -130,31 +213,31 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
130213
if (!isProxy(this)) {
131214
throw new Error('Cannot perform mutations on a snapshot')
132215
}
133-
this.data.length = 0 // empty array
216+
this.data.length = 0
134217
this.index = 0
135218
this.epoch++
136219
indexMap.clear()
137220
},
138-
forEach(cb) {
139-
this.epoch // touch property for tracking
221+
forEach(cb: (value: T, valueAgain: T, set: Set<T>) => void) {
222+
this.epoch
140223
const map = getMapForThis(this)
141224
map.forEach((index) => {
142225
cb(this.data[index]!, this.data[index]!, this)
143226
})
144227
},
145228
*values(): SetIterator<T> {
146-
this.epoch // touch property for tracking
229+
this.epoch
147230
const map = getMapForThis(this)
148231
for (const index of map.values()) {
149232
yield this.data[index]!
150233
}
151234
},
152235
keys(): SetIterator<T> {
153-
this.epoch // touch property for tracking
236+
this.epoch
154237
return this.values()
155238
},
156239
*entries(): SetIterator<[T, T]> {
157-
this.epoch // touch property for tracking
240+
this.epoch
158241
const map = getMapForThis(this)
159242
for (const index of map.values()) {
160243
const value = this.data[index]!
@@ -170,76 +253,25 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
170253
get [Symbol.toStringTag]() {
171254
return 'Set'
172255
},
173-
intersection(other: Set<T>): Set<T> {
174-
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-
)
256+
intersection: intersectionImpl,
257+
union: unionImpl,
258+
difference: differenceImpl,
259+
symmetricDifference: symmetricDifferenceImpl,
260+
isSubsetOf(other: RSetLike<T>) {
261+
this.epoch
262+
for (const v of this.values()) if (!other.has(v)) return false
263+
return true
230264
},
231-
isSupersetOf(other: Set<T>) {
232-
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-
)
265+
isSupersetOf(other: RSetLike<T>) {
266+
this.epoch
267+
const it = asIterable(other)
268+
for (const v of it) if (!this.has(v)) return false
269+
return true
238270
},
239-
isDisjointFrom(other: Set<T>): boolean {
240-
this.epoch // touch property for tracking
241-
const otherSet = proxySet<T>(other)
242-
return [...this.values()].every((value) => !otherSet.has(value))
271+
isDisjointFrom(other: RSetLike<T>) {
272+
this.epoch
273+
for (const v of this.values()) if (other.has(v)) return false
274+
return true
243275
},
244276
}
245277

@@ -253,7 +285,7 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
253285
})
254286
Object.seal(proxiedObject)
255287

256-
return proxiedObject as unknown as InternalProxySet<T> & {
288+
return proxiedObject as InternalProxySet<T> & {
257289
$$valtioSnapshot: Omit<InternalProxySet<T>, 'set' | 'delete' | 'clear'>
258290
}
259291
}

tsconfig.json

Lines changed: 19 additions & 7 deletions
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,
@@ -13,14 +13,26 @@
1313
"verbatimModuleSyntax": true,
1414
"declaration": true,
1515
"isolatedDeclarations": true,
16-
"types": ["@testing-library/jest-dom"],
16+
"types": [
17+
"@testing-library/jest-dom"
18+
],
1719
"noEmit": true,
1820
"baseUrl": ".",
1921
"paths": {
20-
"valtio": ["./src/index.ts"],
21-
"valtio/*": ["./src/*.ts"]
22+
"valtio": [
23+
"./src/index.ts"
24+
],
25+
"valtio/*": [
26+
"./src/*.ts"
27+
]
2228
}
2329
},
24-
"include": ["src/**/*", "tests/**/*"],
25-
"exclude": ["node_modules", "dist"]
26-
}
30+
"include": [
31+
"src/**/*",
32+
"tests/**/*"
33+
],
34+
"exclude": [
35+
"node_modules",
36+
"dist"
37+
]
38+
}

0 commit comments

Comments
 (0)