zustand-lite
Version:
Zustand Lite builds upon zustand, by auto-generating selectors and simplifying API even more.
109 lines (103 loc) • 5.67 kB
TypeScript
import { StoreApi as StoreApi$1 } from 'zustand';
import { DevtoolsOptions, PersistOptions } from 'zustand/middleware';
type State = Record<PropertyKey, unknown>;
type EqualityChecker<S> = (state: S, newState: S) => boolean;
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
type Override<T, U> = Prettify<Omit<T, keyof U> & U>;
type Augments<T, U, Base> = Base & Override<T, U>;
type StoreLib<S> = Omit<StoreApi$1<S>, 'setState' | 'getState'>;
type GetBase<S extends State> = () => Readonly<S>;
type SetBase<S extends State> = StoreApi$1<S>['setState'];
type UseBase<S extends State> = UseRecordBase<S>;
type OverrideGet<T, U, S extends State> = Augments<T, U, GetBase<S>>;
type OverrideSet<T, U, S extends State> = Augments<T, U, SetBase<S>>;
type OverrideUse<T, U, S extends State> = Augments<T, U, UseBase<S>>;
type GetRecord<S extends Record<string, unknown>> = GetBase<S>;
type SetRecord<S extends Record<string, unknown>> = SetBase<S> & {
[K in keyof S]-?: (value: S[K]) => any;
};
type StoreApiEncapsulated<S extends State = {}, Getters = {}, Setters = {}, ExtraMW = {}> = {
api: StoreApi$1<S> & ExtraMW;
get: OverrideGet<GetRecord<S>, Getters, S>;
set: OverrideSet<SetRecord<S>, Setters, S>;
use: OverrideUse<UseRecord<S>, Getters, S>;
};
type StoreApi<S extends State = {}, Getters = {}, Setters = {}, ExtraMW = {}> = {
api: StoreLib<S> & ExtraMW;
get: OverrideGet<GetRecord<S>, Getters, S>;
set: OverrideSet<SetRecord<S>, Setters, S>;
use: OverrideUse<UseRecord<S>, Getters, S>;
composePlugin<P extends (store: StoreApi<S, Getters, Setters, ExtraMW>) => StoreApi<any, any, any, ExtraMW>>(plugin: P): P extends (store: StoreApi<S, Getters, Setters, ExtraMW>) => StoreApi<infer S2, infer G2, infer A2, ExtraMW> ? StoreApi<Override<S, S2>, // merge state
Override<Getters, G2>, // merge getters (aware of new S)
Override<Setters, A2>, // merge setters (aware of new S)
ExtraMW> : never;
extendByState<NS extends State>(patch: NoOverlappingKeys<S, NS>): StoreApi<S & NS, Getters, Setters, ExtraMW>;
extendByState<NS extends State, Builder extends ByStateBuilder<NS, S, Getters>>(builder: ByStateBuilder<NS, S, Getters>): StoreApi<S & ReturnType<Builder>, Getters, Setters, ExtraMW>;
extendGetters<Builder extends GettersBuilder<S, Getters>>(builder: Builder): StoreApi<S, Override<Getters, ReturnType<Builder>>, Setters, ExtraMW>;
extendSetters<Builder extends SettersBuilder<S, Getters, Setters>>(builder: Builder): StoreApi<S, Getters, Override<Setters, ReturnType<Builder>>, ExtraMW>;
restrictState(): StoreApiEncapsulated<S, Getters, Setters, ExtraMW>;
restrictState<Key extends keyof S>(publicState: Key[]): StoreApiEncapsulated<Omit<S, Key>, Getters, Setters, ExtraMW>;
};
type GettersBuilder<S extends State, Getters> = (args: {
get: OverrideGet<GetRecord<S>, Getters, S>;
}) => Record<string, AnyFn>;
type SettersBuilder<S extends State, Getters = {}, Setters = {}> = (args: {
api: StoreLib<S>;
get: OverrideGet<GetRecord<S>, Getters, S>;
set: OverrideSet<SetRecord<S>, Setters, S>;
}) => Record<string, AnyFn>;
type ByStateBuilder<NS extends State, S extends State, Getters = {}> = (args: {
get: OverrideGet<GetRecord<S>, Getters, S>;
}) => NoOverlappingKeys<S, NS>;
type UseRecordBase<S> = {
<K extends readonly (keyof S)[]>(selector: K, equality?: EqualityChecker<Pick<S, K[number]>>): Readonly<Pick<S, K[number]>>;
<R>(selector: (state: S) => R, equality?: EqualityChecker<R>): R;
(selector?: undefined, equality?: EqualityChecker<Readonly<S>>): Readonly<S>;
};
type UseRecord<S> = UseRecordDeep<S> & UseRecordBase<S>;
type AnyFn = (...args: any[]) => any;
type UseRecordDeep<S> = {
[K in keyof S]-?: S[K] extends Record<string, any> ? IsOptional<S, K> extends false ? ((equalityFn?: EqualityChecker<S[K]>) => S[K]) & UseRecordDeep<S[K]> : never : (equalityFn?: EqualityChecker<S[K]>) => S[K];
};
type IsOptional<S, K extends keyof S> = undefined extends S[K] ? {} extends Pick<S, K> ? true : false : false;
type NoOverlappingKeys<Old, New> = keyof Old & keyof New extends never ? New : never;
type MWConfiguration = {
devtools?: true | Omit<DevtoolsOptions, 'store'>;
persist?: true | Omit<PersistOptions<any>, 'name'>;
};
type StorePersist<S> = {
persist: {
clearStorage: () => void;
getOptions: () => Partial<PersistOptions<S, S>>;
hasHydrated: () => boolean;
onFinishHydration: (fn: (state: S) => void) => () => void;
onHydrate: (fn: (state: S) => void) => () => void;
read: () => S | undefined;
rehydrate: () => Promise<void> | void;
setOptions: (options: Partial<PersistOptions<S, S>>) => void;
};
};
type GlobalConfig = {
appName: string;
logging: boolean;
};
/**
* Identity helper that provides a typed `store` param and preserves the plugin's return type.
*/
declare function definePlugin<F extends (store: StoreApi) => StoreApi>(fn: F): <S extends State, G, A, MW>(store: StoreApi<S, G, A, MW>) => ReturnType<F>;
declare function setGlobalConfig(newConfig: Partial<GlobalConfig>): void;
declare function createStore<S extends State, ExtraMW extends MWConfiguration = {}>(initialState: S, options?: {
name?: string;
middlewares?: ExtraMW;
}): StoreApi<S, GetRecord<S>, SetRecord<S>, ExtraMW extends {
persist: any;
} ? StorePersist<S> : {}>;
/**
* Basic plugin example, that extends store with custom setter.
*/
declare const withReset: <S extends State, G, A, MW>(store: StoreApi<S, G, A, MW>) => StoreApi<{}, {}, {
reset: () => void;
}, {}>;
export { createStore, definePlugin, setGlobalConfig, withReset };