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;