Skip to content

Commit

Permalink
improve internal type safety
Browse files Browse the repository at this point in the history
  • Loading branch information
EskiMojo14 committed Feb 4, 2024
1 parent 241003f commit 8adeb42
Showing 1 changed file with 62 additions and 58 deletions.
120 changes: 62 additions & 58 deletions packages/toolkit/src/entities/slice_creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import type {
} from '@reduxjs/toolkit'
import type { PayloadAction } from '../createAction'
import { reducerCreator, type CaseReducerDefinition } from '../createSlice'
import type { CastAny, WithRequiredProp } from '../tsHelpers'
import type { WithRequiredProp } from '../tsHelpers'
import type {
Update,
EntityAdapter,
EntityId,
EntityState,
Expand All @@ -34,13 +35,11 @@ type EntityReducers<
> as `${K}${Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
T,
Id
>[K] extends infer Method
? Method extends CaseReducer<any, PayloadAction>
? CaseReducerDefinition<State, PayloadAction>
: Method extends CaseReducer<any, PayloadAction<infer Payload>>
? CaseReducerDefinition<State, PayloadAction<Payload>>
: never
: never
>[K] extends (state: any) => any
? CaseReducerDefinition<State, PayloadAction>
: EntityStateAdapter<T, Id>[K] extends CaseReducer<any, infer A>
? CaseReducerDefinition<State, A>
: never
}

export interface EntityMethodsCreatorConfig<
Expand All @@ -50,7 +49,7 @@ export interface EntityMethodsCreatorConfig<
Single extends string,
Plural extends string,
> {
selectEntityState?: (state: CastAny<State, unknown>) => EntityState<T, Id>
selectEntityState?: (state: State) => EntityState<T, Id>
name?: Single
pluralName?: Plural
}
Expand Down Expand Up @@ -104,76 +103,81 @@ declare module '@reduxjs/toolkit' {
}
}

export const entityMethodsCreator: ReducerCreator<
typeof entityMethodsCreatorType
> = {
export const entityMethodsCreator = {
type: entityMethodsCreatorType,
create(
adapter,
create<
T,
Id extends EntityId,
State = EntityState<T, Id>,
Single extends string = '',
Plural extends string = DefaultPlural<Single>,
>(
adapter: EntityAdapter<T, Id>,
{
selectEntityState = (state) => state as EntityState<any, any>,
// template literal computed keys don't keep their type if there's an unresolved generic
// so we cast to some intermediate type to at least check we're using the right variables in the right places
name = '' as 's',
pluralName = name && (`${name}s` as 'p'),
}: EntityMethodsCreatorConfig<any, any, any, 's', 'p'> = {},
): EntityReducers<any, any, any, 's', 'p'> {
selectEntityState = (state) => state as EntityState<T, Id>,
name: nameParam = '' as Single,
pluralName: pluralParam = (nameParam && `${nameParam}s`) as Plural,
}: EntityMethodsCreatorConfig<T, Id, State, Single, Plural> = {},
): EntityReducers<T, Id, State, Single, Plural> {
// template literal computed keys don't keep their type if there's an unresolved generic
// so we cast to some intermediate type to at least check we're using the right variables in the right places

const name = nameParam as 's'
const pluralName = pluralParam as 'p'
const reducer = reducerCreator.create
return {
[`addOne${capitalize(name)}` as const]: reducer<any>((state, action) => {
[`addOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
adapter.addOne(selectEntityState(state), action.payload)
}),
[`addMany${capitalize(pluralName)}` as const]: reducer<any>(
(state, action) => {
adapter.addMany(selectEntityState(state), action.payload)
},
),
[`setOne${capitalize(name)}` as const]: reducer<any>((state, action) => {
[`addMany${capitalize(pluralName)}` as const]: reducer<
readonly T[] | Record<Id, T>
>((state, action) => {
adapter.addMany(selectEntityState(state), action.payload)
}),
[`setOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
adapter.setOne(selectEntityState(state), action.payload)
}),
[`setMany${capitalize(pluralName)}` as const]: reducer<any>(
(state, action) => {
adapter.setMany(selectEntityState(state), action.payload)
},
),
[`setAll${capitalize(pluralName)}` as const]: reducer<any>(
(state, action) => {
adapter.setAll(selectEntityState(state), action.payload)
},
),
[`removeOne${capitalize(name)}` as const]: reducer<any>(
[`setMany${capitalize(pluralName)}` as const]: reducer<
readonly T[] | Record<Id, T>
>((state, action) => {
adapter.setMany(selectEntityState(state), action.payload)
}),
[`setAll${capitalize(pluralName)}` as const]: reducer<
readonly T[] | Record<Id, T>
>((state, action) => {
adapter.setAll(selectEntityState(state), action.payload)
}),
[`removeOne${capitalize(name)}` as const]: reducer<Id>(
(state, action) => {
adapter.removeOne(selectEntityState(state), action.payload)
},
),
[`removeMany${capitalize(pluralName)}` as const]: reducer<any>(
[`removeMany${capitalize(pluralName)}` as const]: reducer<readonly Id[]>(
(state, action) => {
adapter.removeMany(selectEntityState(state), action.payload)
},
),
[`removeAll${capitalize(pluralName)}` as const]: reducer((state) => {
adapter.removeAll(selectEntityState(state))
}),
[`upsertOne${capitalize(name)}` as const]: reducer<any>(
(state, action) => {
adapter.upsertOne(selectEntityState(state), action.payload)
},
),
[`upsertMany${capitalize(pluralName)}` as const]: reducer<any>(
(state, action) => {
adapter.upsertMany(selectEntityState(state), action.payload)
},
),
[`updateOne${capitalize(name)}` as const]: reducer<any>(
[`upsertOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
adapter.upsertOne(selectEntityState(state), action.payload)
}),
[`upsertMany${capitalize(pluralName)}` as const]: reducer<
readonly T[] | Record<Id, T>
>((state, action) => {
adapter.upsertMany(selectEntityState(state), action.payload)
}),
[`updateOne${capitalize(name)}` as const]: reducer<Update<T, Id>>(
(state, action) => {
adapter.updateOne(selectEntityState(state), action.payload)
},
),
[`updateMany${capitalize(pluralName)}` as const]: reducer<any>(
(state, action) => {
adapter.updateMany(selectEntityState(state), action.payload)
},
),
}
[`updateMany${capitalize(pluralName)}` as const]: reducer<
readonly Update<T, Id>[]
>((state, action) => {
adapter.updateMany(selectEntityState(state), action.payload)
}),
} satisfies EntityReducers<T, Id, State, 's', 'p'> as any
},
}
} satisfies ReducerCreator<typeof entityMethodsCreatorType>

0 comments on commit 8adeb42

Please sign in to comment.