export type Proxy = T; export type OnceCell = Proxy; export type OnceCellConstructor = Proxy<((create: () => T) => Proxy) & (new (create: () => T) => OnceCell) & { dereference: (cell: OnceCell) => T }>; /** * @license MIT * @copyright memdmp */ export const OnceCell = new Proxy( // We use the function keyword here to indicate that this *may* be a constructor function (create: () => T): OnceCell { 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 }, { 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