-
Notifications
You must be signed in to change notification settings - Fork 2
/
decorators.ts
101 lines (85 loc) · 2.35 KB
/
decorators.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
// Decorators that make working with signals in classes easy.
import {
createMemo,
createSignal,
useBatch,
useUntrack,
type Signal,
} from "./index.js"
export function untrack<This, Value extends (this: This, ...args: any) => any>(
value: Value,
context:
| ClassGetterDecoratorContext<This, ReturnType<Value>>
| ClassMethodDecoratorContext<This, Value>
| ClassSetterDecoratorContext<This, ReturnType<Value>>,
) {
return function (this, ...args) {
return useUntrack(() => value.apply(this, args))
} as Value
}
export function batch<This, Value extends (this: This, ...args: any) => any>(
value: Value,
context:
| ClassGetterDecoratorContext<This, ReturnType<Value>>
| ClassMethodDecoratorContext<This, Value>
| ClassSetterDecoratorContext<This, ReturnType<Value>>,
) {
return function (this, ...args) {
return useBatch(() => value.apply(this, args))
} as Value
}
export function signal<This extends object, Value>(
value: ClassAccessorDecoratorTarget<This, Value>,
context: ClassAccessorDecoratorContext<This, Value>,
): ClassAccessorDecoratorResult<This, Value> {
const map = new WeakMap<This, Signal<Value>>()
function get(self: This) {
const signal = map.get(self)
if (!signal) {
throw new TypeError(
"Cannot access signal value on an uninitialized object.",
)
}
return signal
}
return {
get() {
return get(this)[0]()
},
set(value) {
get(this)[1](value)
},
init(value) {
map.set(this, createSignal(value))
return value
},
}
}
export function memo<This extends object, Value>(
value: (this: This) => Value,
context: ClassGetterDecoratorContext<This, Value>,
) {
const map = new WeakMap<This, () => Value>()
return function (this: This): Value {
let memoed = map.get(this)
if (memoed) {
return memoed()
}
memoed = createMemo(() => value.call(this))
map.set(this, memoed)
return memoed()
}
}
export function effect<Class extends abstract new (...args: any) => any>(
effect: (this: InstanceType<Class>) => void,
) {
return (value: Class, context: ClassDecoratorContext<Class>) => {
abstract class ClassWithEffect extends value {
constructor(...args: any[]) {
super(...args)
effect.call(this as InstanceType<Class>)
}
}
return ClassWithEffect
}
}