UNPKG

9.16 kBPlain TextView Raw
1import { Action, UnknownAction } from './actions'
2import { Reducer } from './reducers'
3// eslint-disable-next-line @typescript-eslint/no-unused-vars
4import _$$observable from '../utils/symbol-observable'
5
6/**
7 * A *dispatching function* (or simply *dispatch function*) is a function that
8 * accepts an action or an async action; it then may or may not dispatch one
9 * or more actions to the store.
10 *
11 * We must distinguish between dispatching functions in general and the base
12 * `dispatch` function provided by the store instance without any middleware.
13 *
14 * The base dispatch function *always* synchronously sends an action to the
15 * store's reducer, along with the previous state returned by the store, to
16 * calculate a new state. It expects actions to be plain objects ready to be
17 * consumed by the reducer.
18 *
19 * Middleware wraps the base dispatch function. It allows the dispatch
20 * function to handle async actions in addition to actions. Middleware may
21 * transform, delay, ignore, or otherwise interpret actions or async actions
22 * before passing them to the next middleware.
23 *
24 * @template A The type of things (actions or otherwise) which may be
25 * dispatched.
26 */
27export interface Dispatch<A extends Action = UnknownAction> {
28 <T extends A>(action: T, ...extraArgs: any[]): T
29}
30
31/**
32 * Function to remove listener added by `Store.subscribe()`.
33 */
34export interface Unsubscribe {
35 (): void
36}
37
38export type ListenerCallback = () => void
39
40declare global {
41 interface SymbolConstructor {
42 readonly observable: symbol
43 }
44}
45
46/**
47 * A minimal observable of state changes.
48 * For more information, see the observable proposal:
49 * https://github.com/tc39/proposal-observable
50 */
51export type Observable<T> = {
52 /**
53 * The minimal observable subscription method.
54 * @param {Object} observer Any object that can be used as an observer.
55 * The observer object should have a `next` method.
56 * @returns {subscription} An object with an `unsubscribe` method that can
57 * be used to unsubscribe the observable from the store, and prevent further
58 * emission of values from the observable.
59 */
60 subscribe: (observer: Observer<T>) => { unsubscribe: Unsubscribe }
61 [Symbol.observable](): Observable<T>
62}
63
64/**
65 * An Observer is used to receive data from an Observable, and is supplied as
66 * an argument to subscribe.
67 */
68export type Observer<T> = {
69 next?(value: T): void
70}
71
72/**
73 * A store is an object that holds the application's state tree.
74 * There should only be a single store in a Redux app, as the composition
75 * happens on the reducer level.
76 *
77 * @template S The type of state held by this store.
78 * @template A the type of actions which may be dispatched by this store.
79 * @template StateExt any extension to state from store enhancers
80 */
81export interface Store<
82 S = any,
83 A extends Action = UnknownAction,
84 StateExt extends unknown = unknown
85> {
86 /**
87 * Dispatches an action. It is the only way to trigger a state change.
88 *
89 * The `reducer` function, used to create the store, will be called with the
90 * current state tree and the given `action`. Its return value will be
91 * considered the **next** state of the tree, and the change listeners will
92 * be notified.
93 *
94 * The base implementation only supports plain object actions. If you want
95 * to dispatch a Promise, an Observable, a thunk, or something else, you
96 * need to wrap your store creating function into the corresponding
97 * middleware. For example, see the documentation for the `redux-thunk`
98 * package. Even the middleware will eventually dispatch plain object
99 * actions using this method.
100 *
101 * @param action A plain object representing “what changed”. It is a good
102 * idea to keep actions serializable so you can record and replay user
103 * sessions, or use the time travelling `redux-devtools`. An action must
104 * have a `type` property which may not be `undefined`. It is a good idea
105 * to use string constants for action types.
106 *
107 * @returns For convenience, the same action object you dispatched.
108 *
109 * Note that, if you use a custom middleware, it may wrap `dispatch()` to
110 * return something else (for example, a Promise you can await).
111 */
112 dispatch: Dispatch<A>
113
114 /**
115 * Reads the state tree managed by the store.
116 *
117 * @returns The current state tree of your application.
118 */
119 getState(): S & StateExt
120
121 /**
122 * Adds a change listener. It will be called any time an action is
123 * dispatched, and some part of the state tree may potentially have changed.
124 * You may then call `getState()` to read the current state tree inside the
125 * callback.
126 *
127 * You may call `dispatch()` from a change listener, with the following
128 * caveats:
129 *
130 * 1. The subscriptions are snapshotted just before every `dispatch()` call.
131 * If you subscribe or unsubscribe while the listeners are being invoked,
132 * this will not have any effect on the `dispatch()` that is currently in
133 * progress. However, the next `dispatch()` call, whether nested or not,
134 * will use a more recent snapshot of the subscription list.
135 *
136 * 2. The listener should not expect to see all states changes, as the state
137 * might have been updated multiple times during a nested `dispatch()` before
138 * the listener is called. It is, however, guaranteed that all subscribers
139 * registered before the `dispatch()` started will be called with the latest
140 * state by the time it exits.
141 *
142 * @param listener A callback to be invoked on every dispatch.
143 * @returns A function to remove this change listener.
144 */
145 subscribe(listener: ListenerCallback): Unsubscribe
146
147 /**
148 * Replaces the reducer currently used by the store to calculate the state.
149 *
150 * You might need this if your app implements code splitting and you want to
151 * load some of the reducers dynamically. You might also need this if you
152 * implement a hot reloading mechanism for Redux.
153 *
154 * @param nextReducer The reducer for the store to use instead.
155 */
156 replaceReducer(nextReducer: Reducer<S, A>): void
157
158 /**
159 * Interoperability point for observable/reactive libraries.
160 * @returns {observable} A minimal observable of state changes.
161 * For more information, see the observable proposal:
162 * https://github.com/tc39/proposal-observable
163 */
164 [Symbol.observable](): Observable<S & StateExt>
165}
166
167export type UnknownIfNonSpecific<T> = {} extends T ? unknown : T
168
169/**
170 * A store creator is a function that creates a Redux store. Like with
171 * dispatching function, we must distinguish the base store creator,
172 * `createStore(reducer, preloadedState)` exported from the Redux package, from
173 * store creators that are returned from the store enhancers.
174 *
175 * @template S The type of state to be held by the store.
176 * @template A The type of actions which may be dispatched.
177 * @template PreloadedState The initial state that is passed into the reducer.
178 * @template Ext Store extension that is mixed in to the Store type.
179 * @template StateExt State extension that is mixed into the state type.
180 */
181export interface StoreCreator {
182 <S, A extends Action, Ext extends {} = {}, StateExt extends {} = {}>(
183 reducer: Reducer<S, A>,
184 enhancer?: StoreEnhancer<Ext, StateExt>
185 ): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
186 <
187 S,
188 A extends Action,
189 Ext extends {} = {},
190 StateExt extends {} = {},
191 PreloadedState = S
192 >(
193 reducer: Reducer<S, A, PreloadedState>,
194 preloadedState?: PreloadedState | undefined,
195 enhancer?: StoreEnhancer<Ext>
196 ): Store<S, A, UnknownIfNonSpecific<StateExt>> & Ext
197}
198
199/**
200 * A store enhancer is a higher-order function that composes a store creator
201 * to return a new, enhanced store creator. This is similar to middleware in
202 * that it allows you to alter the store interface in a composable way.
203 *
204 * Store enhancers are much the same concept as higher-order components in
205 * React, which are also occasionally called “component enhancers”.
206 *
207 * Because a store is not an instance, but rather a plain-object collection of
208 * functions, copies can be easily created and modified without mutating the
209 * original store. There is an example in `compose` documentation
210 * demonstrating that.
211 *
212 * Most likely you'll never write a store enhancer, but you may use the one
213 * provided by the developer tools. It is what makes time travel possible
214 * without the app being aware it is happening. Amusingly, the Redux
215 * middleware implementation is itself a store enhancer.
216 *
217 * @template Ext Store extension that is mixed into the Store type.
218 * @template StateExt State extension that is mixed into the state type.
219 */
220export type StoreEnhancer<Ext extends {} = {}, StateExt extends {} = {}> = <
221 NextExt extends {},
222 NextStateExt extends {}
223>(
224 next: StoreEnhancerStoreCreator<NextExt, NextStateExt>
225) => StoreEnhancerStoreCreator<NextExt & Ext, NextStateExt & StateExt>
226export type StoreEnhancerStoreCreator<
227 Ext extends {} = {},
228 StateExt extends {} = {}
229> = <S, A extends Action, PreloadedState>(
230 reducer: Reducer<S, A, PreloadedState>,
231 preloadedState?: PreloadedState | undefined
232) => Store<S, A, StateExt> & Ext