-
Notifications
You must be signed in to change notification settings - Fork 6
/
lazy.ts
61 lines (52 loc) · 2.25 KB
/
lazy.ts
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
type AnyFn = (...args: unknown[]) => unknown;
type WithToString = { toString: () => string };
/**
* @name lazyMethod
* @description
* Creates a lazy, on-demand getter for the specific value. Upon get the value will be evaluated.
*/
export function lazyMethod <T, K, S> (result: Record<string, T> | AnyFn, item: K, creator: (item: K, index: number, self: S) => T, getName?: (item: K, index: number) => string, index = 0): void {
const name = getName
? getName(item, index)
: (item as WithToString).toString();
let value: T | undefined;
Object.defineProperty(result, name, {
// This allows for re-configuration with the embedded defineProperty below
// and ensures that on tested browsers and Node, it _will_ be redefined
// and thus short-circuited for future access
configurable: true,
enumerable: true,
// Use a function here, we don't want to capture the outer this, i.e.
// don't use arrow functions in this context since we have a this inside
get: function (): T {
// This check should _always_ be false and unneeded, since we override
// with a value below ... however we ensure we are quire vigilant against
// all environment failures, so we are rather be safe than sorry
if (value === undefined) {
value = creator(item, index, this as S);
try {
// re-define the property as a value, next time around this
// getter will only return the computed value
Object.defineProperty(this, name, { value });
} catch {
// ignore any errors, since this _should_ not happen due to
// the "configurable" property above. But if it ever does
// from here-on we will be the cached value the next time
// around (with a very slight dip in performance)
}
}
return value;
}
});
}
/**
* @name lazyMethods
* @description
* Creates lazy, on-demand getters for the specific values.
*/
export function lazyMethods <T, K, S> (result: Record<string, T>, items: readonly K[], creator: (item: K, index: number, self: S) => T, getName?: (item: K, index: number) => string): Record<string, T> {
for (let i = 0; i < items.length; i++) {
lazyMethod(result, items[i], creator, getName, i);
}
return result;
}