type SetStateInternal = { _(partial: T | Partial | { _(state: T): T | Partial }['_'], replace?: false): void _(state: T | { _(state: T): T }['_'], replace: true): void }['_'] export interface StoreApi { setState: SetStateInternal getState: () => T getInitialState: () => T subscribe: (listener: (state: T, prevState: T) => void, immediatelyTrigger?: boolean) => () => void } export type ExtractState = S extends { getState: () => infer T } ? T : never type Get = K extends keyof T ? T[K] : F export type Mutate = number extends Ms['length' & keyof Ms] ? S : Ms extends [] ? S : Ms extends [[infer Mi, infer Ma], ...infer Mrs] ? Mutate[Mi & StoreMutatorIdentifier], Mrs> : never export type StateCreator< T, Mis extends [StoreMutatorIdentifier, unknown][] = [], Mos extends [StoreMutatorIdentifier, unknown][] = [], U = T > = (( setState: Get, Mis>, 'setState', never>, getState: Get, Mis>, 'getState', never>, store: Mutate, Mis> ) => U) & { $$storeMutators?: Mos } // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-object-type export interface StoreMutators {} export type StoreMutatorIdentifier = keyof StoreMutators type CreateStore = { ( initializer: StateCreator ): Mutate, Mos> (): ( initializer: StateCreator ) => Mutate, Mos> } type CreateStoreImpl = ( initializer: StateCreator ) => Mutate, Mos> const createStoreImpl: CreateStoreImpl = (createState) => { type TState = ReturnType type Listener = (state: TState, prevState: TState) => void let state: TState const listeners: Set = new Set() const setState: StoreApi['setState'] = (partial, replace) => { // TODO: Remove type assertion once https://github.com/microsoft/TypeScript/issues/37663 is resolved // https://github.com/microsoft/TypeScript/issues/37663#issuecomment-759728342 const nextState = typeof partial === 'function' ? (partial as (state: TState) => TState)(state) : partial if (!Object.is(nextState, state)) { const previousState = state state = (replace ?? (typeof nextState !== 'object' || nextState === null)) ? (nextState as TState) : Object.assign({}, state, nextState) listeners.forEach((listener) => listener(state, previousState)) } } const getState: StoreApi['getState'] = () => state const getInitialState: StoreApi['getInitialState'] = () => initialState const subscribe: StoreApi['subscribe'] = (listener, immediatelyTrigger) => { listeners.add(listener) if (immediatelyTrigger) listener(state, state) // Unsubscribe return () => listeners.delete(listener) } const api = { setState, getState, getInitialState, subscribe } const initialState = (state = createState(setState, getState, api)) return api as any } export const createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl) as CreateStore