theatre/packages/dataverse/src/pointer.ts

105 lines
2.4 KiB
TypeScript
Raw Normal View History

2021-06-18 13:05:06 +02:00
import type {$IntentionalAny} from './types'
type PathToProp = Array<string | number>
type PointerMeta = {
root: {}
path: (string | number)[]
}
export type UnindexableTypesForPointer =
| number
| string
| boolean
| null
| void
| undefined
| Function // eslint-disable-line @typescript-eslint/ban-types
export type UnindexablePointer = {
[K in $IntentionalAny]: Pointer<undefined>
}
const pointerMetaWeakMap = new WeakMap<{}, PointerMeta>()
export type PointerType<O> = {
$$__pointer_type: O
}
export type Pointer<O> = PointerType<O> &
(O extends UnindexableTypesForPointer
? UnindexablePointer
: unknown extends O
? UnindexablePointer
: O extends (infer T)[]
? Pointer<T>[]
: O extends {}
? {
[K in keyof O]-?: Pointer<O[K]>
} /*&
{[K in string | number]: Pointer<K extends keyof O ? O[K] : undefined>}*/
: UnindexablePointer)
const pointerMetaSymbol = Symbol('pointerMeta')
const cachedSubPointersWeakMap = new WeakMap<
{},
Record<string | number, Pointer<unknown>>
>()
const handler = {
get(obj: {}, prop: string | typeof pointerMetaSymbol): $IntentionalAny {
if (prop === pointerMetaSymbol) return pointerMetaWeakMap.get(obj)!
let subs = cachedSubPointersWeakMap.get(obj)
if (!subs) {
subs = {}
cachedSubPointersWeakMap.set(obj, subs)
}
if (subs[prop]) return subs[prop]
const meta = pointerMetaWeakMap.get(obj)!
const subPointer = pointer({root: meta.root, path: [...meta.path, prop]})
subs[prop] = subPointer
return subPointer
},
}
export const getPointerMeta = (
p: Pointer<$IntentionalAny> | Pointer<{}> | Pointer<unknown>,
): PointerMeta => {
// @ts-ignore @todo
const meta: PointerMeta = p[
pointerMetaSymbol as unknown as $IntentionalAny
] as $IntentionalAny
return meta
}
export const getPointerParts = (
p: Pointer<$IntentionalAny> | Pointer<{}> | Pointer<unknown>,
): {root: {}; path: PathToProp} => {
const {root, path} = getPointerMeta(p)
return {root, path}
}
function pointer<O>({
root,
path,
}: {
root: {}
path: Array<string | number>
}): Pointer<O>
function pointer(args: {root: {}; path?: Array<string | number>}) {
const meta: PointerMeta = {
root: args.root as $IntentionalAny,
path: args.path ?? [],
}
const hiddenObj = {}
pointerMetaWeakMap.set(hiddenObj, meta)
return new Proxy(hiddenObj, handler) as Pointer<$IntentionalAny>
}
export default pointer