-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathdefault-map.ts
More file actions
132 lines (124 loc) · 3.99 KB
/
default-map.ts
File metadata and controls
132 lines (124 loc) · 3.99 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
/**
* A Map that automatically creates default values for missing keys.
*
* @remarks
* Extends the native `Map` class with a `getOrCreate` method that returns
* an existing value or creates a new one using the provided factory function.
*
* @example
* Creating a DefaultMap with a factory function:
* ```typescript
* const map = new DefaultMap<string, number[]>(undefined, () => []);
* map.getOrCreate('items').push(1, 2, 3);
* ```
*
* @example
* Creating a DefaultMap with initial entries:
* ```typescript
* const initial: [string, Set<number>][] = [
* ['evens', new Set([2, 4, 6])],
* ['odds', new Set([1, 3, 5])],
* ];
* const map = new DefaultMap(initial, () => new Set<number>());
* map.getOrCreate('primes').add(2).add(3).add(5);
* ```
*/
class DefaultMap<K, V> extends Map<K, V> {
private readonly _factoryFn: () => V;
public override get [Symbol.toStringTag](): string {
return 'DefaultMap';
}
/**
* Creates a new DefaultMap instance.
*
* @param entries - Optional iterable of key-value pairs to initialize the map.
* @param factoryFn - Factory function that creates default values for missing keys.
* Defaults to creating a new `Map` instance.
*/
constructor(entries?: Iterable<readonly [K, V]>, factoryFn?: () => V) {
super(entries);
this._factoryFn = factoryFn ?? (() => new Map() as V);
}
/**
* Returns the value for the given key, creating it if it doesn't exist.
*
* @param key - The key to look up or create a value for.
* @returns The existing or newly created value for the key.
*/
public getOrCreate(key: K): V {
if (!this.has(key)) {
this.set(key, this._factoryFn());
}
return this.get(key) as V;
}
/**
* Converts the DefaultMap to a plain Map for structured cloning.
*
* @remarks
* This method helps with cross-browser compatibility when using BroadcastChannel
* or postMessage, as custom Map subclasses are not properly cloned in Safari.
* Returns a plain Map that is guarantied to be structured cloneable across all browsers.
*
* @returns A plain Map with the same entries as this DefaultMap.
*
* @example
* ```typescript
* const defaultMap = new DefaultMap<string, Set<number>>();
* const plainMap = defaultMap.toPlainMap();
* channel.postMessage({ data: plainMap });
* ```
*/
public toPlainMap(): Map<K, V> {
return new Map(this.entries());
}
}
/**
* Creates a new DefaultMap with the specified entries and factory function.
*
* @param entries - Optional iterable of key-value pairs to initialize the map.
* @param factoryFn - Factory function that creates default values for missing keys.
* @returns A new DefaultMap instance.
*
* @example
* Creating a DefaultMap with a factory function:
* ```typescript
* const counters = createDefaultMap<string, number>(undefined, () => 0);
* counters.set('visits', counters.getOrCreate('visits') + 1);
* ```
*
* @example
* Creating a DefaultMap with initial entries:
* ```typescript
* const defaults: [string, string[]][] = [
* ['fruits', ['apple', 'banana']],
* ['vegetables', ['carrot', 'broccoli']],
* ];
* const categories = createDefaultMap(defaults, () => [] as string[]);
* categories.getOrCreate('dairy').push('milk', 'cheese');
* ```
*/
export function createDefaultMap<K, V>(
entries?: Iterable<readonly [K, V]>,
factoryFn?: () => V
): DefaultMap<K, V> {
return new DefaultMap<K, V>(entries, factoryFn);
}
/**
* Creates a DefaultMap for icon collections with nested Map values.
*
* @remarks
* This is a convenience function for creating the nested map structure
* used by the icon registry to organize icons by collection and name.
*
* @returns A DefaultMap where each value is itself a Map.
*
* @example
* ```typescript
* const icons = createIconDefaultMap<string, SvgIcon>();
* icons.getOrCreate('material').set('home', { svg: '...' });
* ```
*/
export function createIconDefaultMap<K, V>() {
return new DefaultMap<K, Map<K, V>>();
}
export type { DefaultMap };