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