diff --git a/docs/api/createEntityAdapter.mdx b/docs/api/createEntityAdapter.mdx index 0cfee25b9e..10eb7afbe1 100644 --- a/docs/api/createEntityAdapter.mdx +++ b/docs/api/createEntityAdapter.mdx @@ -118,19 +118,11 @@ export type Comparer = (a: T, b: T) => number export type IdSelector = (model: T) => EntityId -export interface DictionaryNum { - [id: number]: T | undefined -} - -export interface Dictionary extends DictionaryNum { - [id: string]: T | undefined -} - export type Update = { id: EntityId; changes: Partial } export interface EntityState { ids: EntityId[] - entities: Dictionary + entities: Record } export interface EntityDefinition { @@ -183,7 +175,7 @@ export interface EntityStateAdapter { export interface EntitySelectors { selectIds: (state: V) => EntityId[] - selectEntities: (state: V) => Dictionary + selectEntities: (state: V) => Record selectAll: (state: V) => T[] selectTotal: (state: V) => number selectById: (state: V, id: EntityId) => T | undefined @@ -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 @@ -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: { diff --git a/packages/toolkit/src/entities/entity_state.ts b/packages/toolkit/src/entities/entity_state.ts index 8a9c6a895c..62ab918442 100644 --- a/packages/toolkit/src/entities/entity_state.ts +++ b/packages/toolkit/src/entities/entity_state.ts @@ -6,7 +6,7 @@ export function getInitialEntityState(): EntityState< > { return { ids: [], - entities: {}, + entities: {} as Record, } } diff --git a/packages/toolkit/src/entities/index.ts b/packages/toolkit/src/entities/index.ts index 9e2292a24c..e258527474 100644 --- a/packages/toolkit/src/entities/index.ts +++ b/packages/toolkit/src/entities/index.ts @@ -1,6 +1,5 @@ export { createEntityAdapter } from './create_adapter' export type { - Dictionary, EntityState, EntityAdapter, Update, diff --git a/packages/toolkit/src/entities/models.ts b/packages/toolkit/src/entities/models.ts index d20db1c5de..55e5a9779a 100644 --- a/packages/toolkit/src/entities/models.ts +++ b/packages/toolkit/src/entities/models.ts @@ -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 @@ -16,11 +17,6 @@ export type Comparer = (a: T, b: T) => number */ export type IdSelector = (model: T) => Id -/** - * @public - */ -export type Dictionary = Partial> - /** * @public */ @@ -31,7 +27,7 @@ export type Update = { id: Id; changes: Partial } */ export interface EntityState { ids: Id[] - entities: Dictionary + entities: Record } /** @@ -42,10 +38,9 @@ export interface EntityDefinition { sortComparer: false | Comparer } -export type PreventAny = IsAny< +export type PreventAny = CastAny< S, - EntityState, - S + EntityState > /** @@ -157,10 +152,10 @@ export interface EntityStateAdapter { */ export interface EntitySelectors { selectIds: (state: V) => Id[] - selectEntities: (state: V) => Dictionary + selectEntities: (state: V) => Record selectAll: (state: V) => T[] selectTotal: (state: V) => number - selectById: (state: V, id: Id) => T | undefined + selectById: (state: V, id: Id) => Compute> } /** diff --git a/packages/toolkit/src/entities/sorted_state_adapter.ts b/packages/toolkit/src/entities/sorted_state_adapter.ts index 96bb75da4a..7d73513671 100644 --- a/packages/toolkit/src/entities/sorted_state_adapter.ts +++ b/packages/toolkit/src/entities/sorted_state_adapter.ts @@ -61,7 +61,7 @@ export function createSortedStateAdapter( state: R ): void { newEntities = ensureEntitiesArray(newEntities) - state.entities = {} + state.entities = {} as Record state.ids = [] addManyMutably(newEntities, state) diff --git a/packages/toolkit/src/entities/state_selectors.ts b/packages/toolkit/src/entities/state_selectors.ts index 92a0f82378..a647d4f248 100644 --- a/packages/toolkit/src/entities/state_selectors.ts +++ b/packages/toolkit/src/entities/state_selectors.ts @@ -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() { function getSelectors(): EntitySelectors, Id> @@ -27,7 +22,7 @@ export function createSelectorsFactory() { const selectId = (_: unknown, id: Id) => id - const selectById = (entities: Dictionary, id: Id) => entities[id] + const selectById = (entities: Record, id: Id) => entities[id] const selectTotal = createDraftSafeSelector(selectIds, (ids) => ids.length) diff --git a/packages/toolkit/src/entities/unsorted_state_adapter.ts b/packages/toolkit/src/entities/unsorted_state_adapter.ts index 9c3295dc43..77697e1618 100644 --- a/packages/toolkit/src/entities/unsorted_state_adapter.ts +++ b/packages/toolkit/src/entities/unsorted_state_adapter.ts @@ -67,7 +67,7 @@ export function createUnsortedStateAdapter( newEntities = ensureEntitiesArray(newEntities) state.ids = [] - state.entities = {} + state.entities = {} as Record addManyMutably(newEntities, state) } diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts index f30024161f..48ea776850 100644 --- a/packages/toolkit/src/index.ts +++ b/packages/toolkit/src/index.ts @@ -105,7 +105,6 @@ export { Tuple } from './utils' export { createEntityAdapter } from './entities/create_adapter' export type { - Dictionary, EntityState, EntityAdapter, EntitySelectors, diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts index d08ed5dda0..f0ed92a6e1 100644 --- a/packages/toolkit/src/tsHelpers.ts +++ b/packages/toolkit/src/tsHelpers.ts @@ -18,6 +18,8 @@ export type IsAny = // 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 = IsAny + /** * return True if T is `unknown`, otherwise return False * taken from /~https://github.com/joonhocho/tsdef diff --git a/packages/toolkit/src/uncheckedindexed.ts b/packages/toolkit/src/uncheckedindexed.ts new file mode 100644 index 0000000000..628ff5dc43 --- /dev/null +++ b/packages/toolkit/src/uncheckedindexed.ts @@ -0,0 +1,16 @@ +// inlined from /~https://github.com/EskiMojo14/uncheckedindexed +// relies on remaining as a TS file, not .d.ts +type IfMaybeUndefined = [undefined] extends [T] ? True : False + +const testAccess = ({} as Record).a + +export type IfUncheckedIndexedAccess = IfMaybeUndefined< + typeof testAccess, + True, + False +> + +export type UncheckedIndexedAccess = IfUncheckedIndexedAccess< + T | undefined, + T +> diff --git a/packages/toolkit/tsup.config.ts b/packages/toolkit/tsup.config.ts index d4d9219086..4a3aa994fb 100644 --- a/packages/toolkit/tsup.config.ts +++ b/packages/toolkit/tsup.config.ts @@ -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`