nanostores
Version:
A tiny (265 bytes) state manager for React/Preact/Vue/Svelte with many atomic tree-shakable stores
150 lines (132 loc) • 3.86 kB
TypeScript
import type {
AllKeys,
ReadableAtom,
ReadonlyIfObject,
WritableAtom
} from '../atom/index.js'
type KeyofBase = keyof any
type Get<T, K extends KeyofBase> = Extract<T, { [K1 in K]: any }>[K]
export type HasIndexSignature<T> = string extends keyof T ? true : false
export type ValueWithUndefinedForIndexSignatures<
Value,
Key extends keyof Value
> = HasIndexSignature<Value> extends true ? undefined | Value[Key] : Value[Key]
export type WritableStore<Value = any> =
| (Value extends object ? MapStore<Value> : never)
| WritableAtom<Value>
export type Store<Value = any> = ReadableAtom<Value> | WritableStore<Value>
export type AnyStore<Value = any> = {
get(): Value
readonly value: undefined | Value
}
export type StoreValue<SomeStore> = SomeStore extends {
get(): infer Value
}
? Value
: any
export type BaseMapStore<Value = any> = {
setKey: (key: any, value: any) => any
} & WritableAtom<Value>
export type MapStoreKeys<SomeStore> = SomeStore extends {
setKey: (key: infer K, ...args: any[]) => any
}
? K
: AllKeys<StoreValue<SomeStore>>
export interface MapStore<Value extends object = any>
extends WritableAtom<Value> {
/**
* Subscribe to store changes.
*
* In contrast with {@link Store#subscribe} it do not call listener
* immediately.
*
* @param listener Callback with store value and old value.
* @param changedKey Key that was changed. Will present only if `setKey`
* has been used to change a store.
* @returns Function to remove listener.
*/
listen(
listener: (
value: ReadonlyIfObject<Value>,
oldValue: ReadonlyIfObject<Value>,
changedKey: AllKeys<Value>
) => void
): () => void
/**
* Low-level method to notify listeners about changes in the store.
*
* Can cause unexpected behaviour when combined with frontend frameworks
* that perform equality checks for values, such as React.
*/
notify(oldValue?: ReadonlyIfObject<Value>, changedKey?: AllKeys<Value>): void
/**
* Change store value.
*
* ```js
* $settings.set({ theme: 'dark' })
* ```
*
* Operation is atomic, subscribers will be notified once with the new value.
* `changedKey` will be undefined
*
* @param newValue New store value.
*/
set(newValue: Value): void
/**
* Change key in store value.
*
* ```js
* $settings.setKey('theme', 'dark')
* ```
*
* To delete key set `undefined`.
*
* ```js
* $settings.setKey('theme', undefined)
* ```
*
* @param key The key name.
* @param value New value.
*/
setKey<Key extends AllKeys<Value>>(
key: Key,
value: Get<Value, Key> | ValueWithUndefinedForIndexSignatures<Value, Key>
): void
/**
* Subscribe to store changes and call listener immediately.
*
* ```
* import { $router } from '../store'
*
* $router.subscribe(page => {
* console.log(page)
* })
* ```
*
* @param listener Callback with store value and old value.
* @param changedKey Key that was changed. Will present only
* if `setKey` has been used to change a store.
* @returns Function to remove listener.
*/
subscribe(
listener: (
value: ReadonlyIfObject<Value>,
oldValue: ReadonlyIfObject<Value> | undefined,
changedKey: AllKeys<Value> | undefined
) => void
): () => void
}
export interface PreinitializedMapStore<Value extends object = any>
extends MapStore<Value> {
readonly value: Value
}
/**
* Create map store. Map store is a store with key-value object
* as a store value.
*
* @param init Initialize store and return store destructor.
* @returns The store object with methods to subscribe.
*/
export function map<Value extends object, StoreExt extends object = object>(
value?: Value
): PreinitializedMapStore<Value> & StoreExt