-
Notifications
You must be signed in to change notification settings - Fork 0
/
reref.ts
53 lines (42 loc) · 1.45 KB
/
reref.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
const TARGET = Symbol();
function unproxy<T extends Object>(obj: T): T {
// Unwraps a proxy to get the original object.
return obj[TARGET] ? unproxy(obj[TARGET]) : obj;
}
function proxy<T extends Object>(obj: T, target: T): T {
let methods: WeakMap<Function, Function> | null = null;
return new Proxy<T>(obj, {
get: (_, prop) => {
// Return the original object so we can flatten proxies when
// called recursively.
if (prop === TARGET) {
return target;
}
const value = Reflect.get(target, prop);
// Bind methods so the object as context is not lost.
// This unfortunately means you cannot re-bind the context.
if (value instanceof Function) {
const method = unproxy(value);
methods = methods ?? new WeakMap();
if (!methods.has(method)) {
// Functions are also objects, and objects can have props.
// Proxy the original function so props aren't lost when
// binding.
methods.set(method, proxy(method.bind(target), method));
}
return methods.get(method);
}
return value;
},
set:
// Setters aren't necessary if the proxied object is also
// the target.
obj !== target
? (_, prop, value) => Reflect.set(target, prop, value)
: undefined,
});
}
export default function reref<T extends Object>(obj: T): T {
const target = unproxy(obj);
return proxy(target, target);
}