import { Subject, BehaviorSubject } from "rxjs" import { map } from "rxjs/operators" import assign from "lodash/assign" import clone from "lodash/clone" type Dispatch< StoreType, DispatchersType extends Dispatchers, K extends keyof DispatchersType > = { dispatcher: K & string payload: any } export type Dispatchers = { [key: string]: (currentVal: StoreType, payload: any) => Partial } export default class DispatchingStore> { #state$: BehaviorSubject #dispatchers: Dispatchers #dispatches$: Subject> = new Subject() constructor(initialValue: StoreType, dispatchers: DispatchersType) { this.#state$ = new BehaviorSubject(initialValue) this.#dispatchers = dispatchers this.#dispatches$ .pipe(map(({ dispatcher, payload }) => this.#dispatchers[dispatcher](this.value, payload))) .subscribe((val) => { const data = clone(this.value) assign(data, val) this.#state$.next(data) }) } get subject$() { return this.#state$ } get value() { return this.subject$.value } get dispatches$() { return this.#dispatches$ } dispatch({ dispatcher, payload }: Dispatch) { if (!this.#dispatchers[dispatcher]) throw new Error(`Undefined dispatch type '${dispatcher}'`) this.#dispatches$.next({ dispatcher, payload }) } }