Skip to content

Commit 90c89ad

Browse files
committed
feat: make MergedState obsolete and use [key, value] as state value
1 parent c639d1b commit 90c89ad

1 file changed

Lines changed: 35 additions & 42 deletions

File tree

hyper/state.ts

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
import { Keyof as K } from "./util.ts";
1+
export type Subscriber<T> = (value: T) => void;
22

3-
type StateEntries<Obj extends Record<string, State>> = { [k in K<Obj>]: { key: k; value: Obj[k]["value"] } }[K<Obj>];
4-
type MapEntries<Rs extends State[]> = { [k in keyof Rs]: { key: k; value: Rs[k]["value"] } }[number];
5-
6-
export type Subscriber<T> = (value: T, unused?: unknown) => void;
7-
export type KeyedSubscriber<K extends { key: unknown; value: unknown }> = (value: K["value"], key: K["key"]) => void;
8-
export type StateType<R extends State> = R extends State<infer U> ? U : never;
3+
type MergedStateValue<Obj extends Record<string, State>> = {
4+
[key in keyof Obj]: [key: key, value: Obj[key]["value"]];
5+
}[keyof Obj];
96

107
export class ReadonlyState<T = any> {
118
protected subscribers: Subscriber<T>[] = [];
9+
#state: { value: T };
10+
11+
constructor(state: { value: T }) {
12+
this.#state = state;
13+
}
1214

13-
constructor(public value: T) {}
15+
get value(): T {
16+
return this.#state.value;
17+
}
1418

1519
static isState<X>(x: X): x is Extract<X, State | ReadonlyState> {
1620
return x instanceof ReadonlyState;
@@ -34,59 +38,48 @@ export class ReadonlyState<T = any> {
3438
}
3539

3640
export class State<T = any> extends ReadonlyState<T> {
41+
#state: { value: T };
42+
3743
constructor(value: T) {
38-
super(value);
44+
const state = { value };
45+
super(state);
46+
this.#state = state;
3947
}
4048

4149
/**
4250
* Merge multiple states into a single state
4351
*/
44-
static merge<T>(...states: [State<T>, ...State<T>[]]): MergedState<MapEntries<State<T>[]>>;
52+
static merge<T>(...states: [State<T>, ...State<T>[]]): State<[number, T]>;
4553

46-
static merge<RefMap extends { [k: string]: State }>(refs: RefMap): MergedState<StateEntries<RefMap>>;
54+
static merge<RefMap extends { [k: string]: State }>(refs: RefMap): State<MergedStateValue<RefMap>>;
4755

4856
static merge<T, RefMap extends { [k: string]: State }>(
49-
...refs: [State<T> | RefMap, ...State<T>[]]
50-
): State<T> | MergedState<MapEntries<State<T>[]>> | MergedState<StateEntries<RefMap>> {
51-
if (State.isState(refs[0])) {
52-
const ref = new MergedState<MapEntries<State<T>[]>>(refs[0].value);
53-
for (let i = 0; i < refs.length; i++) {
54-
const r = refs[i] as State<T>;
55-
r.listen(x => ref.publish(x, i));
57+
...states: [State<T> | RefMap, ...State<T>[]]
58+
): State<T> | State<[number, T]> | State<MergedStateValue<RefMap>> {
59+
if (State.isState(states[0])) {
60+
const merged = new State<[number, T]>([0, states[0].value]);
61+
for (let index = 0; index < states.length; index++) {
62+
const state = states[index] as State<T>;
63+
state.listen(updated => merged.publish([index, updated]));
5664
}
57-
return ref;
65+
return merged;
5866
} else {
59-
const ref = new MergedState<StateEntries<RefMap>>(null);
60-
const rs = refs[0];
61-
for (const r in rs) rs[r].listen(c => ref.publish(c, r));
62-
return ref;
67+
const obj = states[0];
68+
type MergedValue = MergedStateValue<RefMap>;
69+
const merged = new State<MergedValue>(Object.values(obj)[0]?.value);
70+
for (const key in obj) obj[key].listen(updated => merged.publish([key, updated]));
71+
return merged;
6372
}
6473
}
6574

66-
publish(next: T | Promise<T>, unused?: unknown) {
75+
publish(next: T | Promise<T>) {
6776
return Promise.resolve(next).then(val => {
68-
this.value = val;
77+
this.#state.value = val;
6978
this.subscribers.forEach(subscriber => subscriber(val));
7079
});
7180
}
7281

7382
readonly() {
74-
return new ReadonlyState(this.value);
75-
}
76-
}
77-
78-
export class MergedState<T extends { key: string | number; value: unknown }> extends State<T["value"]> {
79-
// @ts-expect-error
80-
protected subscribers: KeyedSubscriber<T>[];
81-
82-
listen(listener: KeyedSubscriber<T>): void {
83-
this.subscribers.push(listener);
84-
}
85-
86-
publish(value: T["value"] | Promise<T["value"]>, key: T["key"]) {
87-
return Promise.resolve(value).then(val => {
88-
this.value = val;
89-
this.subscribers.forEach(subscriber => subscriber(val, key));
90-
});
83+
return new ReadonlyState(this.#state);
9184
}
9285
}

0 commit comments

Comments
 (0)