Skip to content
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

Add type parameter for Id in createEntityAdapter #2099

Closed
Show file tree
Hide file tree
Changes from 2 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
36 changes: 26 additions & 10 deletions packages/toolkit/src/entities/create_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,48 @@ import type {
Comparer,
IdSelector,
EntityAdapter,
EntityId,
} from './models'
import { createInitialStateFactory } from './entity_state'
import { createSelectorsFactory } from './state_selectors'
import { createSortedStateAdapter } from './sorted_state_adapter'
import { createUnsortedStateAdapter } from './unsorted_state_adapter'

export interface EntityAdapterOptions<T, Id extends EntityId = EntityId> {
selectId?: IdSelector<T, Id>
sortComparer?: false | Comparer<T>
}

type IsEntityId<T> = T extends EntityId ? T : EntityId

type ExtractId<T> = T extends { id: infer Id } ? Id : never

type ExtractEntityId<
T,
O extends EntityAdapterOptions<T>
> = O['selectId'] extends IdSelector<T, infer Id>
? Id
: IsEntityId<ExtractId<T>>

/**
*
* @param options
*
* @public
*/
export function createEntityAdapter<T>(
options: {
selectId?: IdSelector<T>
sortComparer?: false | Comparer<T>
} = {}
): EntityAdapter<T> {
const { selectId, sortComparer }: EntityDefinition<T> = {
export function createEntityAdapter<
T,
O extends EntityAdapterOptions<T> = EntityAdapterOptions<T>
>(options: O = {} as O): EntityAdapter<T, ExtractEntityId<T, O>> {
type Id = ExtractEntityId<T, O>
const { selectId, sortComparer }: EntityDefinition<T, Id> = {
sortComparer: false,
selectId: (instance: any) => instance.id,
selectId: (instance: any) => instance.id as Id,
...options,
}

const stateFactory = createInitialStateFactory<T>()
const selectorsFactory = createSelectorsFactory<T>()
const stateFactory = createInitialStateFactory<T, Id>()
const selectorsFactory = createSelectorsFactory<T, Id>()
const stateAdapter = sortComparer
? createSortedStateAdapter(selectId, sortComparer)
: createUnsortedStateAdapter(selectId)
Expand Down
13 changes: 8 additions & 5 deletions packages/toolkit/src/entities/entity_state.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import type { EntityState } from './models'
import type { EntityId, EntityState } from './models'

export function getInitialEntityState<V>(): EntityState<V> {
export function getInitialEntityState<V, Id extends EntityId>(): EntityState<
V,
Id
> {
return {
ids: [],
entities: {},
}
}

export function createInitialStateFactory<V>() {
function getInitialState(): EntityState<V>
export function createInitialStateFactory<V, Id extends EntityId>() {
function getInitialState(): EntityState<V, Id>
function getInitialState<S extends object>(
additionalState: S
): EntityState<V> & S
): EntityState<V, Id> & S
function getInitialState(additionalState: any = {}): any {
return Object.assign(getInitialEntityState(), additionalState)
}
Expand Down
177 changes: 95 additions & 82 deletions packages/toolkit/src/entities/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,158 +14,171 @@ export type Comparer<T> = (a: T, b: T) => number
/**
* @public
*/
export type IdSelector<T> = (model: T) => EntityId
export type IdSelector<T, Id extends EntityId> = (model: T) => Id

/**
* @public
*/
export interface DictionaryNum<T> {
[id: number]: T | undefined
}
export type Dictionary<T, Id extends EntityId> = Partial<Record<Id, T>>

/**
* @public
*/
export interface Dictionary<T> extends DictionaryNum<T> {
[id: string]: T | undefined
}
export type DictionaryNum<T> = Dictionary<TemplateStringsArray, number>

/**
* @public
*/
export type Update<T> = { id: EntityId; changes: Partial<T> }
export type Update<T, Id extends EntityId> = { id: Id; changes: Partial<T> }

/**
* @public
*/
export interface EntityState<T> {
ids: EntityId[]
entities: Dictionary<T>
export interface EntityState<T, Id extends EntityId = EntityId> {
ids: Id[]
entities: Dictionary<T, Id>
}

/**
* @public
*/
export interface EntityDefinition<T> {
selectId: IdSelector<T>
export interface EntityDefinition<T, Id extends EntityId> {
selectId: IdSelector<T, Id>
sortComparer: false | Comparer<T>
}

export type PreventAny<S, T> = IsAny<S, EntityState<T>, S>
export type PreventAny<S, T, Id extends EntityId> = IsAny<
S,
EntityState<T, Id>,
S
>

/**
* @public
*/
export interface EntityStateAdapter<T> {
addOne<S extends EntityState<T>>(state: PreventAny<S, T>, entity: T): S
addOne<S extends EntityState<T>>(
state: PreventAny<S, T>,
export interface EntityStateAdapter<T, Id extends EntityId = EntityId> {
addOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entity: T
): S
addOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
action: PayloadAction<T>
): S

addMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: readonly T[] | Record<EntityId, T>
addMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: readonly T[] | Record<Id, T>
): S
addMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: PayloadAction<readonly T[] | Record<EntityId, T>>
addMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: PayloadAction<readonly T[] | Record<Id, T>>
): S

setOne<S extends EntityState<T>>(state: PreventAny<S, T>, entity: T): S
setOne<S extends EntityState<T>>(
state: PreventAny<S, T>,
setOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entity: T
): S
setOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
action: PayloadAction<T>
): S
setMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: readonly T[] | Record<EntityId, T>
setMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: readonly T[] | Record<Id, T>
): S
setMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: PayloadAction<readonly T[] | Record<EntityId, T>>
setMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: PayloadAction<readonly T[] | Record<Id, T>>
): S
setAll<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: readonly T[] | Record<EntityId, T>
setAll<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: readonly T[] | Record<Id, T>
): S
setAll<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: PayloadAction<readonly T[] | Record<EntityId, T>>
setAll<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: PayloadAction<readonly T[] | Record<Id, T>>
): S

removeOne<S extends EntityState<T>>(state: PreventAny<S, T>, key: EntityId): S
removeOne<S extends EntityState<T>>(
state: PreventAny<S, T>,
key: PayloadAction<EntityId>
removeOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
key: Id
): S
removeOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
key: PayloadAction<Id>
): S

removeMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
keys: readonly EntityId[]
removeMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
keys: readonly Id[]
): S
removeMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
keys: PayloadAction<readonly EntityId[]>
removeMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
keys: PayloadAction<readonly Id[]>
): S

removeAll<S extends EntityState<T>>(state: PreventAny<S, T>): S
removeAll<S extends EntityState<T, Id>>(state: PreventAny<S, T, Id>): S

updateOne<S extends EntityState<T>>(
state: PreventAny<S, T>,
update: Update<T>
updateOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
update: Update<T, Id>
): S
updateOne<S extends EntityState<T>>(
state: PreventAny<S, T>,
update: PayloadAction<Update<T>>
updateOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
update: PayloadAction<Update<T, Id>>
): S

updateMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
updates: ReadonlyArray<Update<T>>
updateMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
updates: ReadonlyArray<Update<T, Id>>
): S
updateMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
updates: PayloadAction<ReadonlyArray<Update<T>>>
updateMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
updates: PayloadAction<ReadonlyArray<Update<T, Id>>>
): S

upsertOne<S extends EntityState<T>>(state: PreventAny<S, T>, entity: T): S
upsertOne<S extends EntityState<T>>(
state: PreventAny<S, T>,
upsertOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entity: T
): S
upsertOne<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entity: PayloadAction<T>
): S

upsertMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: readonly T[] | Record<EntityId, T>
upsertMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: readonly T[] | Record<Id, T>
): S
upsertMany<S extends EntityState<T>>(
state: PreventAny<S, T>,
entities: PayloadAction<readonly T[] | Record<EntityId, T>>
upsertMany<S extends EntityState<T, Id>>(
state: PreventAny<S, T, Id>,
entities: PayloadAction<readonly T[] | Record<Id, T>>
): S
}

/**
* @public
*/
export interface EntitySelectors<T, V> {
selectIds: (state: V) => EntityId[]
selectEntities: (state: V) => Dictionary<T>
export interface EntitySelectors<T, V, Id extends EntityId = EntityId> {
selectIds: (state: V) => Id[]
selectEntities: (state: V) => Dictionary<T, Id>
selectAll: (state: V) => T[]
selectTotal: (state: V) => number
selectById: (state: V, id: EntityId) => T | undefined
selectById: (state: V, id: Id) => T | undefined
}

/**
* @public
*/
export interface EntityAdapter<T> extends EntityStateAdapter<T> {
selectId: IdSelector<T>
export interface EntityAdapter<T, Id extends EntityId = EntityId>
extends EntityStateAdapter<T, Id> {
selectId: IdSelector<T, Id>
sortComparer: false | Comparer<T>
getInitialState(): EntityState<T>
getInitialState<S extends object>(state: S): EntityState<T> & S
getSelectors(): EntitySelectors<T, EntityState<T>>
getInitialState(): EntityState<T, Id>
getInitialState<S extends object>(state: S): EntityState<T, Id> & S
getSelectors(): EntitySelectors<T, EntityState<T, Id>, Id>
getSelectors<V>(
selectState: (state: V) => EntityState<T>
): EntitySelectors<T, V>
selectState: (state: V) => EntityState<T, Id>
): EntitySelectors<T, V, Id>
}
Loading