/
MagicObject.ts
112 lines (84 loc) · 3.35 KB
/
MagicObject.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
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import type { Constructor } from '../types/classes';
import type { ClosureArgs } from '../types/helpers';
export interface MagicObjectProxy<T> {
target: T;
instance: T;
}
export type MagicObjectConstructor<T extends MagicObject = MagicObject> = Constructor<T> & typeof MagicObject;
export default class MagicObject {
private static __conjuring: boolean;
private static __reservedProperties: WeakMap<typeof MagicObject, Set<string>> = new WeakMap;
protected static isConjuring(): boolean {
return this.__conjuring;
}
private static isMagicReady(): boolean {
return this.__reservedProperties.has(this);
}
private static prepareMagic(): void {
this.__conjuring = true;
this.__reservedProperties.set(this, new Set(Object.getOwnPropertyNames(new this)));
this.__conjuring = false;
}
protected static isReservedProperty(property: string): boolean {
return !!this.__reservedProperties.get(this)?.has(property);
}
declare protected _proxy: MagicObjectProxy<this>;
constructor(...args: ClosureArgs) {
if (this.static().isConjuring()) {
return;
}
if (!this.static().isMagicReady()) {
this.static().prepareMagic();
}
this._proxy = this.createProxy();
this.initialize(...args);
return this._proxy.instance;
}
protected reserveProperty(property: string): void {
Object.assign(this, { [property]: null });
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected initialize(...args: ClosureArgs): void {
//
}
protected static(): MagicObjectConstructor<this> {
return this.constructor as MagicObjectConstructor<this>;
}
protected __get(property: string): unknown {
return Reflect.get(this._proxy.target, property);
}
protected __set(property: string, value: unknown): void {
Reflect.set(this._proxy.target, property, value);
}
protected __delete(property: string): void {
Reflect.deleteProperty(this._proxy.target, property);
}
protected createProxy(): MagicObjectProxy<this> {
const Static = this.static();
return {
target: this,
instance: new Proxy(this, {
get(target, property, receiver) {
if (typeof property !== 'string' || property in target || Static.isReservedProperty(property)) {
return Reflect.get(target, property, receiver);
}
return target.__get(property);
},
set(target, property, value, receiver) {
if (typeof property !== 'string' || property in target || Static.isReservedProperty(property)) {
return Reflect.set(target, property, value, receiver);
}
target.__set(property, value);
return true;
},
deleteProperty(target, property) {
if (typeof property !== 'string' || property in target || Static.isReservedProperty(property)) {
return Reflect.deleteProperty(target, property);
}
target.__delete(property);
return true;
},
}),
};
}
}