import { createAtom, toObserver } from './atom' import type { Atom, Observer, Subscription } from './types' export type StoreAction = (...args: Array) => any export type StoreActionMap = Record export type StoreActionsFactory = (store: { setState: Store['setState'] get: Store['get'] }) => TActions type NonFunction = T extends (...args: Array) => any ? never : T export class Store { private atom: Atom public readonly actions!: TActions constructor(getValue: (prev?: NoInfer) => T) constructor(initialValue: T) constructor( initialValue: NonFunction, actionsFactory: StoreActionsFactory, ) constructor( valueOrFn: T | ((prev?: T) => T), actionsFactory?: StoreActionsFactory, ) { // createAtom has overloads that return ReadonlyAtom for functions and Atom for values // Store always needs Atom for setState, so we assert the return type this.atom = createAtom( valueOrFn as T | ((prev?: NoInfer) => T), ) as Atom // bind for safe destructuring this.get = this.get.bind(this) this.setState = this.setState.bind(this) this.subscribe = this.subscribe.bind(this) if (actionsFactory) { this.actions = actionsFactory(this) } } public setState(updater: (prev: T) => T) { this.atom.set(updater) } public get state() { return this.atom.get() } public get() { return this.state } public subscribe( observerOrFn: Observer | ((value: T) => void), ): Subscription { return this.atom.subscribe(toObserver(observerOrFn)) } } export class ReadonlyStore implements Omit< Store, 'setState' | 'actions' > { private atom: Atom constructor(getValue: (prev?: NoInfer) => T) constructor(initialValue: T) constructor(valueOrFn: T | ((prev?: T) => T)) { // createAtom has overloads that return ReadonlyAtom for functions and Atom for values // Store always needs Atom for setState, so we assert the return type this.atom = createAtom( valueOrFn as T | ((prev?: NoInfer) => T), ) as Atom } public get state() { return this.atom.get() } public get() { return this.state } public subscribe( observerOrFn: Observer | ((value: T) => void), ): Subscription { return this.atom.subscribe(toObserver(observerOrFn)) } } export function createStore( getValue: (prev?: NoInfer) => T, ): ReadonlyStore export function createStore(initialValue: T): Store export function createStore( initialValue: NonFunction, actions: StoreActionsFactory, ): Store export function createStore( valueOrFn: T | ((prev?: T) => T), actions?: StoreActionsFactory, ): Store | Store | ReadonlyStore { if (typeof valueOrFn === 'function') { return new ReadonlyStore(valueOrFn as (prev?: NoInfer) => T) } if (actions) { return new Store(valueOrFn as NonFunction, actions) } return new Store(valueOrFn) }