• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import type { Action, AnyAction } from './actions';
2import type { Reducer } from './reducers';
3import '../utils/symbol-observable';
4
5/**
6 * Extend the state
7 *
8 * This is used by store enhancers and store creators to extend state.
9 * If there is no state extension, it just returns the state, as is, otherwise
10 * it returns the state joined with its extension.
11 *
12 * Reference for future devs:
13 * https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919
14 */
15export type ExtendState<State, Extension> = [Extension] extends [never]
16  ? State
17  : State & Extension;
18
19/**
20 * Internal "virtual" symbol used to make the `CombinedState` type unique.
21 */
22declare const $CombinedState: unique symbol;
23
24/**
25 * State base type for reducers created with `combineReducers()`.
26 *
27 * This type allows the `createStore()` method to infer which levels of the
28 * preloaded state can be partial.
29 *
30 * Because Typescript is really duck-typed, a type needs to have some
31 * identifying property to differentiate it from other types with matching
32 * prototypes for type checking purposes. That's why this type has the
33 * `$CombinedState` symbol property. Without the property, this type would
34 * match any object. The symbol doesn't really exist because it's an internal
35 * (i.e. not exported), and internally we never check its value. Since it's a
36 * symbol property, it's not expected to be unumerable, and the value is
37 * typed as always undefined, so its never expected to have a meaningful
38 * value anyway. It just makes this type distinquishable from plain `{}`.
39 */
40interface EmptyObject {
41  readonly [$CombinedState]?: undefined
42}
43
44export type CombinedState<S> = EmptyObject & S;
45
46/**
47 * Recursively makes combined state objects partial. Only combined state _root
48 * objects_ (i.e. the generated higher level object with keys mapping to
49 * individual reducers) are partial.
50 */
51export type PreloadedState<S> = Required<S> extends EmptyObject
52  ? S extends CombinedState<infer S1>
53      ? {
54          [K in keyof S1]?: S1[K] extends object ? PreloadedState<S1[K]> : S1[K]
55        }
56      : S
57  : {
58      [K in keyof S]: S[K] extends string | number | boolean | symbol
59      ? S[K]
60      : PreloadedState<S[K]>
61    };
62
63/**
64 * A *dispatching function* (or simply *dispatch function*) is a function that
65 * accepts an action or an async action; it then may or may not dispatch one
66 * or more actions to the store.
67 *
68 * We must distinguish between dispatching functions in general and the base
69 * `dispatch` function provided by the store instance without any middleware.
70 *
71 * The base dispatch function *always* synchronously sends an action to the
72 * store's reducer, along with the previous state returned by the store, to
73 * calculate a new state. It expects actions to be plain objects ready to be
74 * consumed by the reducer.
75 *
76 * Middleware wraps the base dispatch function. It allows the dispatch
77 * function to handle async actions in addition to actions. Middleware may
78 * transform, delay, ignore, or otherwise interpret actions or async actions
79 * before passing them to the next middleware.
80 *
81 * @template A The type of things (actions or otherwise) which may be
82 *   dispatched.
83 */
84export interface Dispatch<A extends Action = AnyAction> {
85  <T extends A>(action: T, ...extraArgs: any[]): T
86}
87
88/**
89 * Function to remove listener added by `Store.subscribe()`.
90 */
91export interface Unsubscribe {
92  (): void
93}
94
95declare global {
96  interface SymbolConstructor {
97    readonly observable: symbol
98  }
99}
100
101/**
102 * A minimal observable of state changes.
103 * For more information, see the observable proposal:
104 * https://github.com/tc39/proposal-observable
105 */
106export type Observable<T> = {
107  /**
108   * The minimal observable subscription method.
109   * @param {Object} observer Any object that can be used as an observer.
110   * The observer object should have a `next` method.
111   * @returns {subscription} An object with an `unsubscribe` method that can
112   * be used to unsubscribe the observable from the store, and prevent further
113   * emission of values from the observable.
114   */
115  subscribe: (observer: Observer<T>) => { unsubscribe: Unsubscribe }
116  [Symbol.observable]
117
118  (): Observable<T>
119};
120
121/**
122 * An Observer is used to receive data from an Observable, and is supplied as
123 * an argument to subscribe.
124 */
125export type Observer<T> = {
126  next?(value: T): void
127};
128
129/**
130 * A store is an object that holds the application's state tree.
131 * There should only be a single store in a Redux app, as the composition
132 * happens on the reducer level.
133 *
134 * @template S The type of state held by this store.
135 * @template A the type of actions which may be dispatched by this store.
136 * @template StateExt any extension to state from store enhancers
137 * @template Ext any extensions to the store from store enhancers
138 */
139export interface Store<S = any,
140A extends Action = AnyAction,
141StateExt = never,
142Ext = {}> {
143  /**
144   * Dispatches an action. It is the only way to trigger a state change.
145   *
146   * The `reducer` function, used to create the store, will be called with the
147   * current state tree and the given `action`. Its return value will be
148   * considered the **next** state of the tree, and the change listeners will
149   * be notified.
150   *
151   * The base implementation only supports plain object actions. If you want
152   * to dispatch a Promise, an Observable, a thunk, or something else, you
153   * need to wrap your store creating function into the corresponding
154   * middleware. For example, see the documentation for the `redux-thunk`
155   * package. Even the middleware will eventually dispatch plain object
156   * actions using this method.
157   *
158   * @param action A plain object representing “what changed”. It is a good
159   *   idea to keep actions serializable so you can record and replay user
160   *   sessions, or use the time travelling `redux-devtools`. An action must
161   *   have a `type` property which may not be `undefined`. It is a good idea
162   *   to use string constants for action types.
163   *
164   * @returns For convenience, the same action object you dispatched.
165   *
166   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
167   * return something else (for example, a Promise you can await).
168   */
169  dispatch: Dispatch<A>
170
171  /**
172   * Reads the state tree managed by the store.
173   *
174   * @returns The current state tree of your application.
175   */
176  getState(): S
177
178  /**
179   * Adds a change listener. It will be called any time an action is
180   * dispatched, and some part of the state tree may potentially have changed.
181   * You may then call `getState()` to read the current state tree inside the
182   * callback.
183   *
184   * You may call `dispatch()` from a change listener, with the following
185   * caveats:
186   *
187   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
188   * If you subscribe or unsubscribe while the listeners are being invoked,
189   * this will not have any effect on the `dispatch()` that is currently in
190   * progress. However, the next `dispatch()` call, whether nested or not,
191   * will use a more recent snapshot of the subscription list.
192   *
193   * 2. The listener should not expect to see all states changes, as the state
194   * might have been updated multiple times during a nested `dispatch()` before
195   * the listener is called. It is, however, guaranteed that all subscribers
196   * registered before the `dispatch()` started will be called with the latest
197   * state by the time it exits.
198   *
199   * @param listener A callback to be invoked on every dispatch.
200   * @returns A function to remove this change listener.
201   */
202  subscribe(listener: () => void): Unsubscribe
203
204  /**
205   * Replaces the reducer currently used by the store to calculate the state.
206   *
207   * You might need this if your app implements code splitting and you want to
208   * load some of the reducers dynamically. You might also need this if you
209   * implement a hot reloading mechanism for Redux.
210   *
211   * @param nextReducer The reducer for the store to use instead.
212   */
213  replaceReducer<NewState, NewActions extends Action>(
214    nextReducer: Reducer<NewState, NewActions>
215  ): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext
216
217  /**
218   * Interoperability point for observable/reactive libraries.
219   * @returns {observable} A minimal observable of state changes.
220   * For more information, see the observable proposal:
221   * https://github.com/tc39/proposal-observable
222   */
223  [Symbol.observable]
224
225  (): Observable<S>
226}
227
228/**
229 * A store creator is a function that creates a Redux store. Like with
230 * dispatching function, we must distinguish the base store creator,
231 * `createStore(reducer, preloadedState)` exported from the Redux package, from
232 * store creators that are returned from the store enhancers.
233 *
234 * @template S The type of state to be held by the store.
235 * @template A The type of actions which may be dispatched.
236 * @template Ext Store extension that is mixed in to the Store type.
237 * @template StateExt State extension that is mixed into the state type.
238 */
239export interface StoreCreator {
240  <S, A extends Action, Ext = {}, StateExt = never>(
241    reducer: Reducer<S, A>,
242    enhancer?: StoreEnhancer<Ext, StateExt>
243  ): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
244
245  <S, A extends Action, Ext = {}, StateExt = never>(
246    reducer: Reducer<S, A>,
247    preloadedState?: PreloadedState<S>,
248    enhancer?: StoreEnhancer<Ext>
249  ): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
250}
251
252/**
253 * A store enhancer is a higher-order function that composes a store creator
254 * to return a new, enhanced store creator. This is similar to middleware in
255 * that it allows you to alter the store interface in a composable way.
256 *
257 * Store enhancers are much the same concept as higher-order components in
258 * React, which are also occasionally called “component enhancers”.
259 *
260 * Because a store is not an instance, but rather a plain-object collection of
261 * functions, copies can be easily created and modified without mutating the
262 * original store. There is an example in `compose` documentation
263 * demonstrating that.
264 *
265 * Most likely you'll never write a store enhancer, but you may use the one
266 * provided by the developer tools. It is what makes time travel possible
267 * without the app being aware it is happening. Amusingly, the Redux
268 * middleware implementation is itself a store enhancer.
269 *
270 * @template Ext Store extension that is mixed into the Store type.
271 * @template StateExt State extension that is mixed into the state type.
272 */
273export type StoreEnhancer<Ext = {}, StateExt = never> = (
274  next: StoreEnhancerStoreCreator<Ext, StateExt>
275) => StoreEnhancerStoreCreator<Ext, StateExt>;
276
277export type StoreEnhancerStoreCreator<Ext = {}, StateExt = never> = <S = any,
278A extends Action = AnyAction>(
279  reducer: Reducer<S, A>,
280  preloadedState?: PreloadedState<S>
281) => Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext;