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

Use Record<EntityId, T> instead of Dictionary<T> #3424

Merged
merged 19 commits into from
May 30, 2023
Merged
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
21 changes: 4 additions & 17 deletions docs/api/createEntityAdapter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -118,19 +118,11 @@ export type Comparer<T> = (a: T, b: T) => number

export type IdSelector<T> = (model: T) => EntityId

export interface DictionaryNum<T> {
[id: number]: T | undefined
}

export interface Dictionary<T> extends DictionaryNum<T> {
[id: string]: T | undefined
}

export type Update<T> = { id: EntityId; changes: Partial<T> }

export interface EntityState<T> {
ids: EntityId[]
entities: Dictionary<T>
entities: Record<EntityId, T>
}

export interface EntityDefinition<T> {
Expand Down Expand Up @@ -183,7 +175,7 @@ export interface EntityStateAdapter<T> {

export interface EntitySelectors<T, V> {
selectIds: (state: V) => EntityId[]
selectEntities: (state: V) => Dictionary<T>
selectEntities: (state: V) => Record<EntityId, T>
selectAll: (state: V) => T[]
selectTotal: (state: V) => number
selectById: (state: V, id: EntityId) => T | undefined
Expand Down Expand Up @@ -228,7 +220,6 @@ All three options will insert _new_ entities into the list. However they differ

:::


Each method has a signature that looks like:

```ts no-transpile
Expand Down Expand Up @@ -358,12 +349,8 @@ const booksSlice = createSlice({
},
})

const {
bookAdded,
booksLoading,
booksReceived,
bookUpdated,
} = booksSlice.actions
const { bookAdded, booksLoading, booksReceived, bookUpdated } =
booksSlice.actions

const store = configureStore({
reducer: {
Expand Down
2 changes: 1 addition & 1 deletion packages/toolkit/src/entities/entity_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function getInitialEntityState<T, Id extends EntityId>(): EntityState<
> {
return {
ids: [],
entities: {},
entities: {} as Record<Id, T>,
}
}

Expand Down
1 change: 0 additions & 1 deletion packages/toolkit/src/entities/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export { createEntityAdapter } from './create_adapter'
export type {
Dictionary,
EntityState,
EntityAdapter,
Update,
Expand Down
19 changes: 7 additions & 12 deletions packages/toolkit/src/entities/models.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { UncheckedIndexedAccess } from '../uncheckedindexed'
import type { PayloadAction } from '../createAction'
import type { IsAny } from '../tsHelpers'
import type { CastAny, Id as Compute } from '../tsHelpers'

/**
* @public
Expand All @@ -16,11 +17,6 @@ export type Comparer<T> = (a: T, b: T) => number
*/
export type IdSelector<T, Id extends EntityId> = (model: T) => Id

/**
* @public
*/
export type Dictionary<T, Id extends EntityId> = Partial<Record<Id, T>>

/**
* @public
*/
Expand All @@ -31,7 +27,7 @@ export type Update<T, Id extends EntityId> = { id: Id; changes: Partial<T> }
*/
export interface EntityState<T, Id extends EntityId> {
ids: Id[]
entities: Dictionary<T, Id>
entities: Record<Id, T>
}

/**
Expand All @@ -42,10 +38,9 @@ export interface EntityDefinition<T, Id extends EntityId> {
sortComparer: false | Comparer<T>
}

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

/**
Expand Down Expand Up @@ -157,10 +152,10 @@ export interface EntityStateAdapter<T, Id extends EntityId> {
*/
export interface EntitySelectors<T, V, Id extends EntityId> {
selectIds: (state: V) => Id[]
selectEntities: (state: V) => Dictionary<T, Id>
selectEntities: (state: V) => Record<Id, T>
selectAll: (state: V) => T[]
selectTotal: (state: V) => number
selectById: (state: V, id: Id) => T | undefined
selectById: (state: V, id: Id) => Compute<UncheckedIndexedAccess<T>>
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/toolkit/src/entities/sorted_state_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function createSortedStateAdapter<T, Id extends EntityId>(
state: R
): void {
newEntities = ensureEntitiesArray(newEntities)
state.entities = {}
state.entities = {} as Record<Id, T>
state.ids = []

addManyMutably(newEntities, state)
Expand Down
9 changes: 2 additions & 7 deletions packages/toolkit/src/entities/state_selectors.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import type { Selector } from 'reselect'
import { createDraftSafeSelector } from '../createDraftSafeSelector'
import type {
EntityState,
EntitySelectors,
Dictionary,
EntityId,
} from './models'
import type { EntityState, EntitySelectors, EntityId } from './models'

export function createSelectorsFactory<T, Id extends EntityId>() {
function getSelectors(): EntitySelectors<T, EntityState<T, Id>, Id>
Expand All @@ -27,7 +22,7 @@ export function createSelectorsFactory<T, Id extends EntityId>() {

const selectId = (_: unknown, id: Id) => id

const selectById = (entities: Dictionary<T, Id>, id: Id) => entities[id]
const selectById = (entities: Record<Id, T>, id: Id) => entities[id]

const selectTotal = createDraftSafeSelector(selectIds, (ids) => ids.length)

Expand Down
2 changes: 1 addition & 1 deletion packages/toolkit/src/entities/unsorted_state_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function createUnsortedStateAdapter<T, Id extends EntityId>(
newEntities = ensureEntitiesArray(newEntities)

state.ids = []
state.entities = {}
state.entities = {} as Record<Id, T>

addManyMutably(newEntities, state)
}
Expand Down
1 change: 0 additions & 1 deletion packages/toolkit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ export { Tuple } from './utils'

export { createEntityAdapter } from './entities/create_adapter'
export type {
Dictionary,
EntityState,
EntityAdapter,
EntitySelectors,
Expand Down
2 changes: 2 additions & 0 deletions packages/toolkit/src/tsHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export type IsAny<T, True, False = never> =
// test if we are going the left AND right path in the condition
true | false extends (T extends never ? true : false) ? True : False

export type CastAny<T, CastTo> = IsAny<T, CastTo, T>

/**
* return True if T is `unknown`, otherwise return False
* taken from https://github.com/joonhocho/tsdef
Expand Down
16 changes: 16 additions & 0 deletions packages/toolkit/src/uncheckedindexed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// inlined from https://github.com/EskiMojo14/uncheckedindexed
// relies on remaining as a TS file, not .d.ts
type IfMaybeUndefined<T, True, False> = [undefined] extends [T] ? True : False

const testAccess = ({} as Record<string, 0>).a

export type IfUncheckedIndexedAccess<True, False> = IfMaybeUndefined<
typeof testAccess,
True,
False
>

export type UncheckedIndexedAccess<T> = IfUncheckedIndexedAccess<
T | undefined,
T
>
9 changes: 9 additions & 0 deletions packages/toolkit/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,15 @@ export default defineConfig((options) => {
if (format === 'cjs' && name === 'production.min') {
writeCommonJSEntry(outputFolder, prefix)
} else if (generateTypedefs) {
if (folder === '') {
// we need to delete the declaration file and replace it with the original source file
fs.rmSync(path.join(outputFolder, 'uncheckedindexed.d.ts'))

fs.copyFileSync(
'src/uncheckedindexed.ts',
path.join(outputFolder, 'uncheckedindexed.ts')
)
}
// TODO Copy/generate `.d.mts` files?
// const inputTypedefsFile = `${outputFilename}.d.ts`
// const outputTypedefsFile = `${outputFilename}.d.mts`
Expand Down