Skip to content

RFC add store.withCurriedTypes helper #684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions etc/redux-toolkit.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,31 @@

import { Action } from 'redux';
import { ActionCreator } from 'redux';
import { AdvancedComponentDecorator } from 'react-redux';
import { AnyAction } from 'redux';
import { ConnectOptions } from 'react-redux';
import { default as createNextState } from 'immer';
import { createSelector } from 'reselect';
import { current } from 'immer';
import { DeepPartial } from 'redux';
import { Dispatch } from 'redux';
import { Draft } from 'immer';
import { InferableComponentEnhancer } from 'react-redux';
import { InferableComponentEnhancerWithProps } from 'react-redux';
import { MapDispatchToPropsNonObject } from 'react-redux';
import { MapDispatchToPropsParam } from 'react-redux';
import { MapStateToPropsParam } from 'react-redux';
import { MergeProps } from 'react-redux';
import { Middleware } from 'redux';
import { Options } from 'react-redux';
import { OutputParametricSelector } from 'reselect';
import { OutputSelector } from 'reselect';
import { ParametricSelector } from 'reselect';
import { Reducer } from 'redux';
import { ReducersMapObject } from 'redux';
import { ResolveThunks } from 'react-redux';
import { Selector } from 'reselect';
import { SelectorFactory } from 'react-redux';
import { Store } from 'redux';
import { StoreEnhancer } from 'redux';
import { ThunkAction } from 'redux-thunk';
Expand Down Expand Up @@ -170,8 +181,16 @@ export interface CreateSliceOptions<State = any, CR extends SliceCaseReducers<St
reducers: ValidateSliceCaseReducers<State, CR>;
}

// @beta (undocumented)
export const createThunk: <Args extends any[], R, State = unknown, Extra = unknown, Dispatch = ThunkDispatch<State, Extra, AnyAction>>(thunkActionCreator: ThunkActionCreator<Args, R, State, Extra, Dispatch>) => ThunkActionCreator<Args, R, State, Extra, Dispatch>;

export { current }

// @beta (undocumented)
export function curryForStoreType<Store extends {
[storeDescriptionKey]: StoreDescription;
}>(): CurryType<Store[typeof storeDescriptionKey]>;

// @public (undocumented)
export interface Dictionary<T> extends DictionaryNum<T> {
// (undocumented)
Expand All @@ -182,6 +201,12 @@ export { Draft }

// @public
export interface EnhancedStore<S = any, A extends Action = AnyAction, M extends Middlewares<S> = Middlewares<S>> extends Store<S, A> {
// (undocumented)
[storeDescriptionKey]: {
RootState: S;
Dispatch: DispatchForMiddlewares<M> & Dispatch<A>;
ThunkExtraArgument: ExtraFromMiddlewares<M>;
};
dispatch: DispatchForMiddlewares<M> & Dispatch<A>;
}

Expand Down
53 changes: 53 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@types/json-stringify-safe": "^5.0.0",
"@types/nanoid": "^2.1.0",
"@types/node": "^10.14.4",
"@types/react-redux": "^7.1.9",
"axios": "^0.19.2",
"console-testing-library": "^0.3.1",
"eslint-config-react-app": "^5.0.1",
Expand Down
10 changes: 8 additions & 2 deletions src/configureStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import {
composeWithDevTools,
EnhancerOptions as DevToolsOptions
} from './devtoolsExtension'

import { storeDescriptionKey } from './curriedTypes'
import isPlainObject from './isPlainObject'
import {
ThunkMiddlewareFor,
curryGetDefaultMiddleware,
CurriedGetDefaultMiddleware
} from './getDefaultMiddleware'
import { DispatchForMiddlewares } from './tsHelpers'
import { DispatchForMiddlewares, ExtraFromMiddlewares } from './tsHelpers'

const IS_PRODUCTION = process.env.NODE_ENV === 'production'

Expand Down Expand Up @@ -110,6 +110,12 @@ export interface EnhancedStore<
* @inheritdoc
*/
dispatch: DispatchForMiddlewares<M> & Dispatch<A>

[storeDescriptionKey]: {
RootState: S
Dispatch: DispatchForMiddlewares<M> & Dispatch<A>
ThunkExtraArgument: ExtraFromMiddlewares<M>
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/createAsyncThunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const miniSerializeError = (value: any): SerializedError => {
return { message: String(value) }
}

type AsyncThunkConfig = {
export type AsyncThunkConfig = {
state?: unknown
dispatch?: Dispatch
extra?: unknown
Expand Down Expand Up @@ -191,7 +191,7 @@ type AsyncThunkActionCreator<
: (arg: ThunkArg) => AsyncThunkAction<Returned, ThunkArg, ThunkApiConfig>
>

interface AsyncThunkOptions<
export interface AsyncThunkOptions<
ThunkArg = void,
ThunkApiConfig extends AsyncThunkConfig = {}
> {
Expand Down
25 changes: 25 additions & 0 deletions src/createThunk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ThunkDispatch } from 'redux-thunk'
import { AnyAction } from 'redux'

export type ThunkActionCreator<
Args extends any[],
R,
State,
Extra,
Dispatch
> = (
...args: Args
) => (dispatch: Dispatch, getState: () => State, extra: Extra) => R

/**
* @beta
*/
export const createThunk = <
Args extends any[],
R,
State = unknown,
Extra = unknown,
Dispatch = ThunkDispatch<State, Extra, AnyAction>
>(
thunkActionCreator: ThunkActionCreator<Args, R, State, Extra, Dispatch>
) => thunkActionCreator
53 changes: 53 additions & 0 deletions src/curriedTypes/RTK.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export interface CurryableTypes {
createAsyncThunk: typeof createAsyncThunk
createThunk: typeof createThunk
}

export interface CurriedType<Args extends StoreDescription> {
createAsyncThunk: CurriedCreateAsyncThunk<
Args['RootState'],
Args['Dispatch'],
Args['ThunkExtraArgument']
>
createThunk: CurriedCreateThunk<
Args['RootState'],
Args['Dispatch'],
Args['ThunkExtraArgument']
>
}

/* eslint-disable import/first */
import { StoreDescription } from './'
import {
createAsyncThunk,
AsyncThunk,
AsyncThunkPayloadCreator,
AsyncThunkConfig,
AsyncThunkOptions
} from '../createAsyncThunk'
import { createThunk, ThunkActionCreator } from '../createThunk'

type CurriedCreateAsyncThunk<RootState, Dispatch, Extra> = <
Returned,
ThunkArg = void,
ThunkApiConfig extends Omit<
AsyncThunkConfig,
'dispatch' | 'state' | 'extra'
> = {}
>(
typePrefix: string,
payloadCreator: AsyncThunkPayloadCreator<
Returned,
ThunkArg,
ThunkApiConfig & {
dispatch: Dispatch
state: RootState
extra: Extra
}
>,
options?: AsyncThunkOptions<ThunkArg, ThunkApiConfig>
) => AsyncThunk<Returned, ThunkArg, ThunkApiConfig>

type CurriedCreateThunk<RootState, Dispatch, Extra> = <Args extends any[], R>(
thunkActionCreator: ThunkActionCreator<Args, R, RootState, Extra, Dispatch>
) => ThunkActionCreator<Args, R, RootState, Extra, Dispatch>
50 changes: 50 additions & 0 deletions src/curriedTypes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
CurriedType as RRCurriedType,
CurryableTypes as RRCurryableType
} from './react-redux'
import {
CurriedType as RTKCurriedType,
CurryableTypes as RTKCurryableType
} from './RTK'

import { UnionToIntersection } from '../tsHelpers'

export declare const storeDescriptionKey: unique symbol
/**
* @beta
*/
export function curryForStoreType<
Store extends { [storeDescriptionKey]: StoreDescription }
>(): CurryType<Store[typeof storeDescriptionKey]> {
return (curry: any) => curry
}

export interface StoreDescription {
Dispatch: any
RootState: any
ThunkExtraArgument: any
}

type CurrySingleType<Args extends StoreDescription> = UnionToIntersection<
{
[K in keyof CurryableTypes]: (
curry: CurryableTypes[K]
) => CurriedType<Args>[K]
}[keyof CurryableTypes]
>

type CurryMultipleTypes<Args extends StoreDescription> = {
<Obj extends Partial<CurryableTypes>>(obj: Obj): Pick<
CurriedType<Args>,
keyof Obj & keyof CurriedType<any>
>
}

export type CurryType<Args extends StoreDescription> = CurrySingleType<Args> &
CurryMultipleTypes<Args>

export interface CurryableTypes extends RRCurryableType, RTKCurryableType {}

export interface CurriedType<Args extends StoreDescription>
extends RRCurriedType<Args['RootState'], Args['Dispatch']>,
RTKCurriedType<Args> {}
Loading