Open EskiMojo14 opened 9 months ago
General gist of what a module system for builders would have to look like:
type KeysWithReducerDefinition<
CaseReducers extends SliceCaseReducers<any>,
Type extends ReducerType
> = KeysMatching<CaseReducers, ReducerDefinition<Type>>
interface SliceReducerCreators<
State = any,
CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
Name extends string = string
> {
reducer: {
create(
caseReducer: CaseReducer<State, PayloadAction>
): CaseReducerDefinition<State, PayloadAction>
create<Payload>(
caseReducer: CaseReducer<State, PayloadAction<Payload>>
): CaseReducerDefinition<State, PayloadAction<Payload>>
actions: {
[Type in KeysWithReducerDefinition<
CaseReducers,
ReducerType.reducer
>]: CaseReducers[Type] extends CaseReducerDefinition<State, any>
? ActionCreatorForCaseReducer<
CaseReducers[Type],
SliceActionType<Name, Type>
>
: never
}
caseReducers: {
[Type in KeysWithReducerDefinition<
CaseReducers,
ReducerType.reducer
>]: CaseReducers[Type] extends CaseReducerDefinition<State, infer A>
? CaseReducer<State, A>
: never
}
}
preparedReducer: {
create<Prepare extends PrepareAction<any>>(
prepare: Prepare,
reducer: CaseReducer<
State,
ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
>
): CaseReducerWithPrepareDefinition<State, Prepare>
actions: {
[Type in KeysWithReducerDefinition<
CaseReducers,
ReducerType.reducerWithPrepare
>]: CaseReducers[Type] extends CaseReducerWithPrepareDefinition<
State,
infer Prepare
>
? _ActionCreatorWithPreparedPayload<
Prepare,
SliceActionType<Name, Type>
>
: never
}
caseReducers: {
[Type in KeysWithReducerDefinition<
CaseReducers,
ReducerType.reducerWithPrepare
>]: CaseReducers[Type] extends CaseReducerWithPrepareDefinition<
State,
infer Prepare
>
? CaseReducer<
State,
ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
>
: never
}
}
asyncThunk: {
create: AsyncThunkCreator<State>
actions: {
[Type in KeysWithReducerDefinition<
CaseReducers,
ReducerType.asyncThunk
>]: CaseReducers[Type] extends AsyncThunkSliceReducerDefinition<
State,
infer ThunkArg,
infer Returned,
infer ThunkApiConfig
>
? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
: never
}
caseReducers: {
[Type in KeysWithReducerDefinition<
CaseReducers,
ReducerType.asyncThunk
>]: CaseReducers[Type] extends AsyncThunkSliceReducerDefinition<
State,
infer ThunkArg,
infer Returned,
infer ThunkApiConfig
>
? Id<
Pick<
Required<
AsyncThunkSliceReducerConfig<
State,
ThunkArg,
Returned,
ThunkApiConfig
>
>,
'fulfilled' | 'pending' | 'rejected' | 'settled'
>
>
: never
}
}
}
type Creators<State> = {
[Name in keyof SliceReducerCreators]: SliceReducerCreators<State>[Name]['create']
}
type CounterCases = {
increment: CaseReducerDefinition<number, PayloadAction>
decrementBy: CaseReducerWithPrepareDefinition<
number,
(amount: number) => { payload: number }
>
fetchAmount: AsyncThunkSliceReducerDefinition<number, void>
}
type Actions = Id<
UnionToIntersection<
SliceReducerCreators<number, CounterCases, 'counter'>[
| 'asyncThunk'
| 'reducer'
| 'preparedReducer']['actions']
>
>
type CaseReducers = Id<
UnionToIntersection<
SliceReducerCreators<number, CounterCases, 'counter'>[
| 'asyncThunk'
| 'reducer'
| 'preparedReducer']['caseReducers']
>
>
courtesy of @phryneas
With the addition of the create callback syntax for createSlice in 2.0, it could be useful to allow custom builders of some fashion to be passed to createSlice (or a function that makes a createSlice function)
creators would need to return a definition which is built immediately when createSlice is called, and then handle that definition by adding any action creators, case reducers, and matchers necessary.
for example, the new create.asyncThunk creator:
type-wise, because the state type is dependent on the individual createSlice call, the only way i could see it working would be with a module augmentation based system like buildCreateApi uses.
this would also be a potential solution for createSlice always pulling in createAsyncThunk for the create.asyncThunk builder - though we are investigating simpler solutions for 2.0.