UNPKG

@amadeus-it-group/tansu

Version:

tansu is a lightweight, push-based framework-agnostic state management library. It borrows the ideas and APIs originally designed and implemented by Svelte stores and extends them with computed and batch.

235 lines (234 loc) 9.7 kB
declare global { interface SymbolConstructor { readonly observable: symbol; } } /** * A callback invoked when a store value changes. It is called with the latest value of a given store. */ export type SubscriberFunction<T> = ((value: T) => void) & Partial<Omit<SubscriberObject<T>, 'next'>>; /** * A partial {@link https://github.com/tc39/proposal-observable#api | observer} notified when a store value changes. A store will call the {@link SubscriberObject.next | next} method every time the store's state is changing. */ export interface SubscriberObject<T> { /** * A store will call this method every time the store's state is changing. */ next: SubscriberFunction<T>; /** * Unused, only declared for compatibility with rxjs. */ error?: any; /** * Unused, only declared for compatibility with rxjs. */ complete?: any; /** * A store will call this method when it knows that the value will be changed. * A call to this method will be followed by a call to {@link SubscriberObject.next | next} or to {@link SubscriberObject.resume | resume}. */ pause: () => void; /** * A store will call this method if {@link SubscriberObject.pause | pause} was called previously * and the value finally did not need to change. */ resume: () => void; } /** * Expresses interest in store value changes over time. It can be either: * - a callback function: {@link SubscriberFunction}; * - a partial observer: {@link SubscriberObject}. */ export type Subscriber<T> = SubscriberFunction<T> | Partial<SubscriberObject<T>> | null | undefined; /** * A function to unsubscribe from value change notifications. */ export type UnsubscribeFunction = () => void; /** * An object with the `unsubscribe` method. * Subscribable stores might choose to return such object instead of directly returning {@link UnsubscribeFunction} from a subscription call. */ export interface UnsubscribeObject { /** * A method that acts as the {@link UnsubscribeFunction}. */ unsubscribe: UnsubscribeFunction; } export type Unsubscriber = UnsubscribeObject | UnsubscribeFunction; /** * Represents a store accepting registrations (subscribers) and "pushing" notifications on each and every store value change. */ export interface SubscribableStore<T> { /** * A method that makes it possible to register "interest" in store value changes over time. * It is called each and every time the store's value changes. * * A registered subscriber is notified synchronously with the latest store value. * * @param subscriber - a subscriber in a form of a {@link SubscriberFunction} or a {@link SubscriberObject}. Returns a {@link Unsubscriber} (function or object with the `unsubscribe` method) that can be used to unregister and stop receiving notifications of store value changes. * @returns The {@link UnsubscribeFunction} or {@link UnsubscribeObject} that can be used to unsubscribe (stop state change notifications). */ subscribe(subscriber: Subscriber<T>): Unsubscriber; } /** * An interface for interoperability between observable implementations. It only has to expose the `[Symbol.observable]` method that is supposed to return a subscribable store. */ export interface InteropObservable<T> { [Symbol.observable]: () => SubscribableStore<T>; } /** * Valid types that can be considered as a store. */ export type StoreInput<T> = SubscribableStore<T> | InteropObservable<T>; /** * Represents a store that can return its value with a get method. */ export interface SignalStore<T> { /** * Returns the value of the store. */ get(): T; } /** * This interface augments the base {@link SubscribableStore} interface by requiring the return value of the subscribe method to be both a function and an object with the `unsubscribe` method. * * For {@link https://rxjs.dev/api/index/interface/InteropObservable | interoperability with rxjs}, it also implements the `[Symbol.observable]` method. */ export interface Readable<T> extends SubscribableStore<T>, InteropObservable<T>, SignalStore<T> { subscribe(subscriber: Subscriber<T>): UnsubscribeFunction & UnsubscribeObject; [Symbol.observable](): Readable<T>; } /** * This interface augments the base {@link Readable} interface by adding the ability to call the store as a function to get its value. */ export interface ReadableSignal<T> extends Readable<T> { /** * Returns the value of the store. */ (): T; } /** * A function that can be used to update store's value. This function is called with the current value and should return new store value. */ export type Updater<T, U = T> = (value: T) => U; /** * Builds on top of {@link Readable} and represents a store that can be manipulated from "outside": anyone with a reference to writable store can either update or completely replace state of a given store. * * @example * * ```typescript * // reset counter's store value to 0 by using the {@link Writable.set} method * counterStore.set(0); * * // increment counter's store value by using the {@link Writable.update} method * counterStore.update(currentValue => currentValue + 1); * ``` */ export interface Writable<T, U = T> extends Readable<T> { /** * Replaces store's state with the provided value. * @param value - value to be used as the new state of a store. */ set(value: U): void; /** * Updates store's state by using an {@link Updater} function. * @param updater - a function that takes the current state as an argument and returns the new state. */ update(updater: Updater<T, U>): void; } /** * Represents a store that implements both {@link ReadableSignal} and {@link Writable}. * This is the type of objects returned by {@link writable}. */ export interface WritableSignal<T, U = T> extends ReadableSignal<T>, Writable<T, U> { } export interface OnUseArgument<T> { (value: T): void; set: (value: T) => void; update: (updater: Updater<T>) => void; } /** * Type of a function that is called when the number of subscribers changes from 0 to 1 * (but not called when the number of subscribers changes from 1 to 2, ...). * * If it returns a function, that function will be called when the number of subscribers changes from 1 to 0. */ export type OnUseFn<T> = (arg: OnUseArgument<T>) => void | Unsubscriber; /** * Store options that can be passed to {@link readable} or {@link writable}. */ export interface StoreOptions<T> { /** * A function that is called when the number of subscribers changes from 0 to 1 * (but not called when the number of subscribers changes from 1 to 2, ...). * If it returns a function, that function will be called when the number of subscribers changes from 1 to 0. */ onUse?: OnUseFn<T>; /** * Custom function to compare two values, that should return true if they * are equal. * * It is called when setting a new value to avoid doing anything * (such as notifying subscribers) if the value did not change. * * @remarks * The default logic (when this option is not present) is to return false * if `a` is a function or an object, or if `a` and `b` are different * according to `Object.is`. * * {@link StoreOptions.equal|equal} takes precedence over {@link StoreOptions.notEqual|notEqual} if both * are defined. * * @param a - First value to compare. * @param b - Second value to compare. * @returns true if a and b are considered equal. */ equal?: (a: T, b: T) => boolean; /** * Custom function to compare two values, that should return true if they * are different. * * It is called when setting a new value to avoid doing anything * (such as notifying subscribers) if the value did not change. * * @remarks * The default logic (when this option is not present) is to return true * if `a` is a function or an object, or if `a` and `b` are different * according to `Object.is`. * * {@link StoreOptions.equal} takes precedence over {@link StoreOptions.notEqual|notEqual} if both * are defined. * * @deprecated Use {@link StoreOptions.equal} instead * @param a - First value to compare. * @param b - Second value to compare. * @returns true if a and b are considered different. */ notEqual?: (a: T, b: T) => boolean; } /** * Either a single {@link StoreInput} or a read-only array of at least one {@link StoreInput}. */ export type StoresInput = StoreInput<any> | readonly [StoreInput<any>, ...StoreInput<any>[]]; /** * Extracts the types of the values of the stores from a type extending {@link StoresInput}. * * @remarks * * If the type given as a parameter is a single {@link StoreInput}, the type of the value * of that {@link StoreInput} is returned. * * If the type given as a parameter is one of an array of {@link StoreInput}, the returned type * is the type of an array containing the value of each store in the same order. */ export type StoresInputValues<S> = S extends StoreInput<infer T> ? T : { [K in keyof S]: S[K] extends StoreInput<infer T> ? T : never; }; export type SyncDeriveFn<T, S> = (values: StoresInputValues<S>) => T; export interface SyncDeriveOptions<T, S> extends Omit<StoreOptions<T>, 'onUse'> { derive: SyncDeriveFn<T, S>; } export type AsyncDeriveFn<T, S> = (values: StoresInputValues<S>, set: OnUseArgument<T>) => Unsubscriber | void; export interface AsyncDeriveOptions<T, S> extends Omit<StoreOptions<T>, 'onUse'> { derive: AsyncDeriveFn<T, S>; }