@rbxts/create-slice
Version:
Helpful function to easily define rodux reducers.
141 lines (127 loc) • 4.13 kB
TypeScript
import Rodux from "@rbxts/rodux"
import { Equals, Extends, If, IsAny } from "./utils/utils"
/**
* Infer the return type of a CaseReducer
*/
type InferReturnType<T> = T extends CaseReducer<any, any, infer R> ? R : never
/**
* Rodux action with a generic payload property. If the payload's type is void, Rodux.AnyAction will be used instead.
*/
export type PayloadAction<Payload> = If<
Equals<Payload, void>,
Rodux.AnyAction,
Rodux.Action & { payload: Payload }
>
/**
* Options for `createSlice`
*/
export interface CreateSliceOptions<
State,
Name extends string,
CaseReducers extends SliceCaseReducers<State>,
> {
/**
* The slice's name. Used to namespace the generated action types.
*/
name: Name
/**
* The initial state that should be used when the reducer is called the first time.
* This may also be a "lazy initializer" function, which should return an initial state value when called.
* This will be used whenever the reducer is called with `undefined` as its state value.
*/
initialState: State | (() => State)
/**
* A mapping from action types to action-type-specific *case reducer* functions.
* For every action type, a matching action creator will be generated using `makeActionCreator()`.
*/
reducers: CaseReducers
}
/**
* The return value of `createSlice
*/
export interface Slice<
State,
Name extends string = string,
CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
> {
/**
* The slice's name.
*/
name: Name
/**
* The slice's reducer.
*/
reducer: SliceReducer<State, Name, CaseReducers>
/**
* The individual case reducer functions that were passed in the `reducers` parameter.
* This enables reuse and testing if they were defined inline when calling `createSlice`.
*/
caseReducers: ValidateCaseReducers<State, CaseReducers>
/**
* Action creators for the types of actions that are handled by the slice reducer.
*/
actions: CaseReducerActionCreators<Name, CaseReducers>
}
/**
* Case Reducers
*/
export type CaseReducer<State, Payload = any, Return extends State | void = State | void> = (
this: void,
state: State,
payload: PayloadAction<Payload>,
) => Return
export type SliceCaseReducers<State, Payload = any> = Record<string, CaseReducer<State, Payload>>
/**
* Assign each case reducer to the CaseReducer type. Without this, defining a reducer
* as `(state) => {}` would infer action to PayloadAction<unknown> instead of PayloadAction<void>
*/
type ValidateCaseReducers<S, ACR extends SliceCaseReducers<S>> = {
[K in keyof ACR]: If<
Extends<Parameters<ACR[K]>["length"], 0 | 1>,
CaseReducer<S, void, InferReturnType<ACR[K]>>, // 0-1 params means no action param
ACR[K] extends CaseReducer<S, infer P, infer R>
? If<IsAny<P>, CaseReducer<S, any, R>, CaseReducer<S, P, R>>
: never
>
}
/**
* Creates a Rodux ActionCreator for a given CaseReducer
*/
type MakeCaseAction<N extends string, CR extends CaseReducer<any, any>> = If<
Extends<Parameters<CR>["length"], 0 | 1>,
Rodux.ActionCreator<N, [], {}>,
CR extends CaseReducer<any, infer P>
? If<
IsAny<P>,
Rodux.ActionCreator<N, [payload?: any], { payload: any }>,
Rodux.ActionCreator<N, [payload: P], { payload: P }>
>
: never
>
/**
* Combines the name of a slice with the name of an action with a "/" between them.
*/
type SliceActionName<SliceName extends string, ActionName extends keyof any> = ActionName extends
| string
| number
? `${SliceName}/${ActionName}`
: string
/**
* Creates a union of all Rodux Actions for a group of caseReducers
*/
type SliceReducerActions<N extends string, ACR extends SliceCaseReducers<any>> = {
[K in keyof ACR]: ReturnType<MakeCaseAction<SliceActionName<N, K>, ACR[K]>>
}[keyof ACR]
/**
* The slice's reducer
*/
type SliceReducer<S, N extends string, ACR extends SliceCaseReducers<S>> = Rodux.Reducer<
S,
SliceReducerActions<N, ACR>
>
/**
* Creates an interface with a Rodux ActionCreator for each caseReducer
*/
type CaseReducerActionCreators<N extends string, ACR extends SliceCaseReducers<any>> = {
[K in keyof ACR]: MakeCaseAction<SliceActionName<N, K>, ACR[K]>
}