Skip to content

Commit 191fce2

Browse files
feat(FactoryMap): add.
1 parent a433920 commit 191fce2

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed

src/map/factory-map.class.ts

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// Class.
2+
import { Data } from '../lib/data.class';
3+
// Abstract.
4+
import { CoreMap } from './core-map.abstract';
5+
import { DataCore } from '../lib/data-core.abstract';
6+
// Type.
7+
import { DataConstructor, MapTypeConstructor } from '../interface';
8+
/**
9+
* @description
10+
* @export
11+
* @class FactoryMap
12+
* @template Key
13+
* @template Value
14+
* @template {Map<Key, Value>} [MapType=Map<Key, Value>]
15+
* @template {DataCore<MapType>} [DataType=Data<MapType>]
16+
* @extends {CoreMap<Key, Value, MapType, DataType>}
17+
*/
18+
export class FactoryMap<
19+
Key,
20+
Value,
21+
MapType extends Map<Key, Value> = Map<Key, Value>,
22+
DataType extends DataCore<MapType> = Data<MapType>,
23+
> extends CoreMap<Key, Value, MapType, DataType> {
24+
/**
25+
* @description
26+
* @public
27+
* @static
28+
* @template {PropertyKey} Key
29+
* @template Value
30+
* @template {Map<Key, Value>} [MapType=Map<Key, Value>]
31+
* @template {DataCore<MapType>} [DataType=Data<MapType>]
32+
* @param {Record<Key, Value>} obj
33+
* @param {?MapTypeConstructor<Key, Value, MapType>} [map]
34+
* @param {?DataConstructor<MapType, DataType>} [data]
35+
* @param {{
36+
* defaultValue?: () => Value;
37+
* cloner?: (value: Value) => Value;
38+
* }} [param0={}]
39+
* @param {() => Value} param0.defaultValue
40+
* @param {(value: Value) => Value} param0.cloner
41+
* @returns {FactoryMap<Key, Value, MapType, DataType>}
42+
*/
43+
public static fromObject<
44+
Key extends PropertyKey,
45+
Value,
46+
MapType extends Map<Key, Value> = Map<Key, Value>,
47+
DataType extends DataCore<MapType> = Data<MapType>
48+
>(
49+
obj: Record<Key, Value>,
50+
map?: MapTypeConstructor<Key, Value, MapType>,
51+
data?: DataConstructor<MapType, DataType>,
52+
{ defaultValue, cloner }:
53+
{
54+
defaultValue?: () => Value;
55+
cloner?: (value: Value) => Value;
56+
} = {}
57+
): FactoryMap<Key, Value, MapType, DataType> {
58+
return new FactoryMap(
59+
Object.entries(obj) as [Key, Value][],
60+
map,
61+
data,
62+
{ cloner, defaultValue }
63+
);
64+
}
65+
66+
/**
67+
* @description Returns the `string` tag representation of the `FactoryMap` class when used in `Object.prototype.toString.call(instance)`.
68+
* @public
69+
* @readonly
70+
*/
71+
public override get [Symbol.toStringTag](): string {
72+
return FactoryMap.name;
73+
}
74+
75+
/**
76+
* @description Returns the privately stored data class.
77+
* @public
78+
* @readonly
79+
* @type {DataType}
80+
*/
81+
public override get data() {
82+
return super.data;
83+
}
84+
85+
/**
86+
* @description Returns the default value.
87+
* @public
88+
* @readonly
89+
* @type {Value | undefined}
90+
*/
91+
public get defaultValue() {
92+
return this.#defaultValue?.();
93+
}
94+
95+
/**
96+
* @description Privately stored function to set the default value in missing keys.
97+
* @public
98+
* @readonly
99+
* @type {() => Value}
100+
*/
101+
#defaultValue?: () => Value;
102+
103+
/**
104+
* @description Privately stored cloner function to clone the returned value.
105+
* @public
106+
* @readonly
107+
* @type {(value: Value) => Value}
108+
*/
109+
#cloner?: (value: Value) => Value;
110+
111+
/**
112+
* Creates an instance of `FactoryMap`.
113+
* @constructor
114+
* @param {?[Key, Value][]} [entries]
115+
* @param {?MapTypeConstructor<Key, Value, MapType>} [map]
116+
* @param {?DataConstructor<MapType, DataType>} [data]
117+
* @param {{
118+
* cloner?: (value: Value) => Value,
119+
* defaultValue?: () => Value,
120+
* }} [param0={}]
121+
* @param {(value: Value) => Value} param0.cloner
122+
* @param {() => Value} param0.defaultValue
123+
*/
124+
constructor(
125+
entries?: [Key, Value][],
126+
map?: MapTypeConstructor<Key, Value, MapType>,
127+
data?: DataConstructor<MapType, DataType>,
128+
{cloner, defaultValue}: {
129+
cloner?: (value: Value) => Value,
130+
defaultValue?: () => Value,
131+
} = {}
132+
) {
133+
super(
134+
entries,
135+
map,
136+
data
137+
);
138+
this.#defaultValue = defaultValue;
139+
this.#cloner = cloner;
140+
}
141+
142+
/**
143+
* @inheritdoc
144+
* @public
145+
* @param {Key} key
146+
* @returns {(Value | undefined)}
147+
*/
148+
public override get(key: Key): Value | undefined {
149+
(!super.has(key) && typeof this.#defaultValue === 'function') && super.data.value.set(key, this.#defaultValue());
150+
const value = super.get(key);
151+
return typeof this.#cloner === 'function' && value ? this.#cloner(value) : value;
152+
}
153+
154+
155+
/**
156+
* @description Gets the cloner function, to use for e.g. `structuredClone`.
157+
* @public
158+
* @returns {((value: Value) => Value) | undefined}
159+
*/
160+
public getCloner() {
161+
return this.#cloner;
162+
}
163+
164+
/**
165+
* @description Returns the default value function.
166+
* @public
167+
* @returns {(() => Value) | undefined}
168+
*/
169+
public getDefaultValue() {
170+
return this.#defaultValue;
171+
}
172+
173+
/**
174+
* @description Sets the cloner function, to use for e.g. `structuredClone`.
175+
* @public
176+
* @param {(value: Value) => Value} clonerFn
177+
* @returns {this} The current instance of `FactoryMap`.
178+
* @example
179+
* ```ts
180+
* const map = new FactoryMap<string, number>();
181+
* map.setCloner((value) => structuredClone(value));
182+
* console.log(map.get('key')); // undefined
183+
* map.set('key', { a: 1 });
184+
* console.log(map.get('key')); // { a: 1 }
185+
* map.set('key', { a: 2 });
186+
* console.log(map.get('key')); // { a: 2 }
187+
* ```
188+
*/
189+
public setCloner(clonerFn: (value: Value) => Value): this {
190+
this.#cloner = clonerFn;
191+
return this;
192+
}
193+
194+
/**
195+
* @description Sets the default value function.
196+
* @public
197+
* @param {() => Value} valueFn
198+
* @returns {this} The current instance of `FactoryMap`.
199+
* @example
200+
* ```ts
201+
* const map = new FactoryMap<string, number>();
202+
* map.setDefaultValue(() => 0);
203+
* console.log(map.get('key')); // 0
204+
* map.set('key', 1);
205+
* console.log(map.get('key')); // 1
206+
* ```
207+
*/
208+
public setDefaultValue(valueFn: () => Value): this {
209+
this.#defaultValue = valueFn;
210+
return this;
211+
}
212+
}

0 commit comments

Comments
 (0)