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
|
export type Proxy<T> = T;
export type OnceCell<T> = Proxy<T & {
/**
* Do not use.
* @deprecated Implementation Detail.
* @internal @private @ignore
*/
'*internal.oncecelldereference': T
}>;
export type OnceCellConstructor = Proxy<(<T>(create: () => T) => Proxy<T>) & (new <T>(create: () => T) => OnceCell<T>) & {
dereference: <T>(cell: OnceCell<T>) => T
}>;
/**
* @license MIT
* @copyright memdmp <memdmp@estrogen.zone>
*/
export const OnceCell = new Proxy(
// We use the function keyword here to indicate that this *may* be a constructor
function <T>(create: () => T): OnceCell<T> {
if (typeof create !== 'function') throw new Error('Must pass function to OnceCell')
let createOnce = (): T extends object ? T : never => {
const t = create();
return (createOnce = () => t as T extends object ? T : never)()
}
const onceCellProxyImplementation: ProxyHandler<{
get value(): T
}> = {
get(_, idx, receiver) {
const target = createOnce();
if (idx === '*internal.oncecelldereference') return target;
// .valueOf() and similar prototype methods should be proxied properly
if (!Object.hasOwnProperty.call(target, idx) && typeof target[idx as keyof T] === 'function') {
return (...args: unknown[]) => {
// @ts-expect-error we don't know .call's argument types, so we just hope and pray they're good and leave it to the caller to deal with it if it isn't
return target[idx as keyof T].call(target, args);
}
}
// return Reflect.get(obj, idx, receiver)
return target[idx as keyof T];
},
set(_, ...args) {
return Reflect.set(createOnce(), ...args)
},
has(_, idx, ...args) {
if (idx === '*internal.oncecelldereference') return true // to comply with the OnceCell type declaration's meaning
return Reflect.has(createOnce(), idx, ...args)
},
deleteProperty(_, ...args) {
return Reflect.deleteProperty(createOnce(), ...args)
},
isExtensible(_, ...args) {
return Reflect.isExtensible(createOnce(), ...args)
},
ownKeys(_, ...args) {
return Reflect.ownKeys(createOnce(), ...args)
},
defineProperty(_, ...args) {
return Reflect.defineProperty(createOnce(), ...args)
},
getOwnPropertyDescriptor(_, ...args) {
return Reflect.getOwnPropertyDescriptor(createOnce(), ...args)
},
preventExtensions(_, ...args) {
return Reflect.preventExtensions(createOnce(), ...args)
},
getPrototypeOf(_, ...args) {
return Reflect.getPrototypeOf(createOnce(), ...args)
},
setPrototypeOf(_, ...args) {
return Reflect.setPrototypeOf(createOnce(), ...args)
},
apply(_, ...args) {
type F = (this: any, ...args: any[]) => any
return Reflect.apply(createOnce() as unknown as T extends F ? F : never, ...args)
},
construct(_, ...args) {
type F = (this: any, ...args: any[]) => any
return Reflect.construct(createOnce() as unknown as T extends F ? F : never, ...args)
},
}
return new Proxy({
/** For debuggers */
get value() {
return createOnce()
}
}, onceCellProxyImplementation) as unknown as OnceCell<T>
}, {
construct(onceCellCreationFunction, argArray, newTarget) {
return onceCellCreationFunction(argArray[0])
},
}) as OnceCellConstructor
OnceCell.dereference = (t) => Reflect.get(t as object, '*internal.oncecelldereference', t)
Object.freeze(OnceCell)
export default OnceCell
|