-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmap-set.ts
More file actions
175 lines (164 loc) · 7.6 KB
/
Copy pathmap-set.ts
File metadata and controls
175 lines (164 loc) · 7.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* @file Safe references to `Map`, `Set`, `WeakMap`, `WeakSet`, and `WeakRef`.
* Constructors plus uncurried prototype methods. `WeakRef` exposes only its
* constructor — there's a separate `weakRefSafe` wrapper in `./uncurry` for
* the throws-on-non-Object case.
*/
import { TypeErrorCtor } from './error'
import { uncurryThis } from './uncurry'
// Stage 3+ TC39 proposals that Node 22+ ships but TypeScript's
// lib.es2024.* still lacks. Ambient-declare here until the lib catches
// up. References:
// - getOrInsert: https://github.com/tc39/proposal-upsert
// - Set composition: https://github.com/tc39/proposal-set-methods
declare global {
interface Map<K, V> {
getOrInsert(key: K, value: V): V
getOrInsertComputed(key: K, callbackfn: (key: K) => V): V
}
interface WeakMap<K extends WeakKey, V> {
getOrInsert(key: K, value: V): V
getOrInsertComputed(key: K, callbackfn: (key: K) => V): V
}
interface ReadonlySetLike<T> {
has(value: T): boolean
keys(): IterableIterator<T>
readonly size: number
}
interface Set<T> {
difference<U>(other: ReadonlySetLike<U>): Set<T>
intersection<U>(other: ReadonlySetLike<U>): Set<T & U>
isDisjointFrom(other: ReadonlySetLike<unknown>): boolean
isSubsetOf(other: ReadonlySetLike<unknown>): boolean
isSupersetOf(other: ReadonlySetLike<unknown>): boolean
symmetricDifference<U>(other: ReadonlySetLike<U>): Set<T | U>
union<U>(other: ReadonlySetLike<U>): Set<T | U>
}
}
// ─── Constructors ──────────────────────────────────────────────────────
export const MapCtor: MapConstructor = Map
export const SetCtor: SetConstructor = Set
export const WeakMapCtor: WeakMapConstructor = WeakMap
export const WeakRefCtor: WeakRefConstructor = WeakRef
export const WeakSetCtor: WeakSetConstructor = WeakSet
// ─── Map (prototype) ───────────────────────────────────────────────────
export const MapPrototypeClear = uncurryThis(Map.prototype.clear)
export const MapPrototypeDelete = uncurryThis(Map.prototype.delete)
export const MapPrototypeEntries = uncurryThis(Map.prototype.entries)
export const MapPrototypeForEach = uncurryThis(Map.prototype.forEach)
export const MapPrototypeGet = uncurryThis(Map.prototype.get)
// getOrInsert / getOrInsertComputed (tc39 proposal-upsert) ship unflagged
// only from Node 25 — engines allows >=22, so each export falls back to a
// spec-equivalent built from the captured primordials when the native is
// absent.
export const MapPrototypeGetOrInsert =
Map.prototype.getOrInsert === undefined
? mapGetOrInsertFallback
: uncurryThis(Map.prototype.getOrInsert)
export const MapPrototypeGetOrInsertComputed =
Map.prototype.getOrInsertComputed === undefined
? mapGetOrInsertComputedFallback
: uncurryThis(Map.prototype.getOrInsertComputed)
export const MapPrototypeHas = uncurryThis(Map.prototype.has)
export const MapPrototypeKeys = uncurryThis(Map.prototype.keys)
export const MapPrototypeSet = uncurryThis(Map.prototype.set)
export const MapPrototypeValues = uncurryThis(Map.prototype.values)
// ─── Set (prototype) ───────────────────────────────────────────────────
export const SetPrototypeAdd = uncurryThis(Set.prototype.add)
export const SetPrototypeClear = uncurryThis(Set.prototype.clear)
export const SetPrototypeDelete = uncurryThis(Set.prototype.delete)
export const SetPrototypeDifference = uncurryThis(Set.prototype.difference)
export const SetPrototypeEntries = uncurryThis(Set.prototype.entries)
export const SetPrototypeForEach = uncurryThis(Set.prototype.forEach)
export const SetPrototypeHas = uncurryThis(Set.prototype.has)
export const SetPrototypeIntersection = uncurryThis(Set.prototype.intersection)
export const SetPrototypeIsDisjointFrom = uncurryThis(
Set.prototype.isDisjointFrom,
)
export const SetPrototypeIsSubsetOf = uncurryThis(Set.prototype.isSubsetOf)
export const SetPrototypeIsSupersetOf = uncurryThis(Set.prototype.isSupersetOf)
export const SetPrototypeKeys = uncurryThis(Set.prototype.keys)
export const SetPrototypeSymmetricDifference = uncurryThis(
Set.prototype.symmetricDifference,
)
export const SetPrototypeUnion = uncurryThis(Set.prototype.union)
export const SetPrototypeValues = uncurryThis(Set.prototype.values)
// ─── WeakMap (prototype) ───────────────────────────────────────────────
export const WeakMapPrototypeDelete = uncurryThis(WeakMap.prototype.delete)
export const WeakMapPrototypeGet = uncurryThis(WeakMap.prototype.get)
export const WeakMapPrototypeGetOrInsert =
WeakMap.prototype.getOrInsert === undefined
? weakMapGetOrInsertFallback
: uncurryThis(WeakMap.prototype.getOrInsert)
export const WeakMapPrototypeGetOrInsertComputed =
WeakMap.prototype.getOrInsertComputed === undefined
? weakMapGetOrInsertComputedFallback
: uncurryThis(WeakMap.prototype.getOrInsertComputed)
export const WeakMapPrototypeHas = uncurryThis(WeakMap.prototype.has)
export const WeakMapPrototypeSet = uncurryThis(WeakMap.prototype.set)
// ─── WeakSet (prototype) ───────────────────────────────────────────────
export const WeakSetPrototypeAdd = uncurryThis(WeakSet.prototype.add)
export const WeakSetPrototypeDelete = uncurryThis(WeakSet.prototype.delete)
export const WeakSetPrototypeHas = uncurryThis(WeakSet.prototype.has)
// ─── proposal-upsert fallbacks ─────────────────────────────────────────
// Hoisted function declarations referenced by the conditional exports
// above. Each mirrors the proposal's algorithm: existing entry wins,
// otherwise insert (computing via the callback, which — per spec — is
// only invoked on a miss, and whose result is stored even if the
// callback itself touched the map).
export function mapGetOrInsertComputedFallback<K, V>(
map: Map<K, V>,
key: K,
callbackfn: (key: K) => V,
): V {
if (typeof callbackfn !== 'function') {
throw new TypeErrorCtor(
`getOrInsertComputed takes a callback. Saw ${typeof callbackfn}, wanted a function computing the value to insert.`,
)
}
if (MapPrototypeHas(map, key)) {
return MapPrototypeGet(map, key) as V
}
const value = callbackfn(key)
MapPrototypeSet(map, key, value)
return value
}
export function mapGetOrInsertFallback<K, V>(
map: Map<K, V>,
key: K,
value: V,
): V {
if (MapPrototypeHas(map, key)) {
return MapPrototypeGet(map, key) as V
}
MapPrototypeSet(map, key, value)
return value
}
export function weakMapGetOrInsertComputedFallback<K extends WeakKey, V>(
map: WeakMap<K, V>,
key: K,
callbackfn: (key: K) => V,
): V {
if (typeof callbackfn !== 'function') {
throw new TypeErrorCtor(
`getOrInsertComputed takes a callback. Saw ${typeof callbackfn}, wanted a function computing the value to insert.`,
)
}
if (WeakMapPrototypeHas(map, key)) {
return WeakMapPrototypeGet(map, key) as V
}
const value = callbackfn(key)
WeakMapPrototypeSet(map, key, value)
return value
}
export function weakMapGetOrInsertFallback<K extends WeakKey, V>(
map: WeakMap<K, V>,
key: K,
value: V,
): V {
if (WeakMapPrototypeHas(map, key)) {
return WeakMapPrototypeGet(map, key) as V
}
WeakMapPrototypeSet(map, key, value)
return value
}