Skip to content

Commit 99d1cf6

Browse files
committed
Merge branch 'v2.0-integration' into customise-create-selector
2 parents 0c9e0e1 + a005d57 commit 99d1cf6

14 files changed

+81
-62
lines changed

docs/api/createEntityAdapter.mdx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ import {
4646

4747
type Book = { bookId: string; title: string }
4848

49-
const booksAdapter = createEntityAdapter<Book>({
49+
const booksAdapter = createEntityAdapter({
5050
// Assume IDs are stored in a field other than `book.id`
51-
selectId: (book) => book.bookId,
51+
selectId: (book: Book) => book.bookId,
5252
// Keep the "all IDs" array sorted based on book titles
5353
sortComparer: (a, b) => a.title.localeCompare(b.title),
5454
})
@@ -118,19 +118,11 @@ export type Comparer<T> = (a: T, b: T) => number
118118

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

121-
export interface DictionaryNum<T> {
122-
[id: number]: T | undefined
123-
}
124-
125-
export interface Dictionary<T> extends DictionaryNum<T> {
126-
[id: string]: T | undefined
127-
}
128-
129121
export type Update<T> = { id: EntityId; changes: Partial<T> }
130122

131123
export interface EntityState<T> {
132124
ids: EntityId[]
133-
entities: Dictionary<T>
125+
entities: Record<EntityId, T>
134126
}
135127

136128
export interface EntityDefinition<T> {
@@ -183,7 +175,7 @@ export interface EntityStateAdapter<T> {
183175

184176
export interface EntitySelectors<T, V> {
185177
selectIds: (state: V) => EntityId[]
186-
selectEntities: (state: V) => Dictionary<T>
178+
selectEntities: (state: V) => Record<EntityId, T>
187179
selectAll: (state: V) => T[]
188180
selectTotal: (state: V) => number
189181
selectById: (state: V, id: EntityId) => T | undefined

packages/toolkit/src/configureStore.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export interface ConfigureStoreOptions<
8282
* and should return a Tuple of enhancers (such as `getDefaultEnhancers().concat(offline)`).
8383
* If you only need to add middleware, you can use the `middleware` parameter instead.
8484
*/
85-
enhancers?: ((getDefaultEnhancers: GetDefaultEnhancers<M>) => E) | E
85+
enhancers?: (getDefaultEnhancers: GetDefaultEnhancers<M>) => E
8686
}
8787

8888
export type Middlewares<S> = ReadonlyArray<Middleware<{}, S>>
@@ -172,13 +172,18 @@ export function configureStore<
172172
const middlewareEnhancer = applyMiddleware(...finalMiddleware)
173173

174174
const getDefaultEnhancers = buildGetDefaultEnhancers<M>(middlewareEnhancer)
175+
176+
if (!IS_PRODUCTION && enhancers && typeof enhancers !== 'function') {
177+
throw new Error('"enhancers" field must be a callback')
178+
}
179+
175180
let storeEnhancers =
176-
(typeof enhancers === 'function'
181+
typeof enhancers === 'function'
177182
? enhancers(getDefaultEnhancers)
178-
: enhancers) ?? getDefaultEnhancers()
183+
: getDefaultEnhancers()
179184

180185
if (!IS_PRODUCTION && !Array.isArray(storeEnhancers)) {
181-
throw new Error('enhancers must be an array')
186+
throw new Error('"enhancers" callback must return an array')
182187
}
183188
if (
184189
!IS_PRODUCTION &&
@@ -194,7 +199,7 @@ export function configureStore<
194199
!storeEnhancers.includes(middlewareEnhancer)
195200
) {
196201
console.error(
197-
'middlewares were provided, but middleware enhancer was not included in final enhancers'
202+
'middlewares were provided, but middleware enhancer was not included in final enhancers - make sure to call `getDefaultEnhancers`'
198203
)
199204
}
200205

packages/toolkit/src/entities/entity_state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export function getInitialEntityState<T, Id extends EntityId>(): EntityState<
66
> {
77
return {
88
ids: [],
9-
entities: {},
9+
entities: {} as Record<Id, T>,
1010
}
1111
}
1212

packages/toolkit/src/entities/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
export { createEntityAdapter } from './create_adapter'
22
export type {
3-
Dictionary,
43
EntityState,
54
EntityAdapter,
65
Update,

packages/toolkit/src/entities/models.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import type { UncheckedIndexedAccess } from '../uncheckedindexed'
12
import type { PayloadAction } from '../createAction'
2-
import type { IsAny } from '../tsHelpers'
33
import type { GetSelectorsOptions } from './state_selectors'
4+
import type { CastAny, Id as Compute } from '../tsHelpers'
45

56
/**
67
* @public
@@ -17,11 +18,6 @@ export type Comparer<T> = (a: T, b: T) => number
1718
*/
1819
export type IdSelector<T, Id extends EntityId> = (model: T) => Id
1920

20-
/**
21-
* @public
22-
*/
23-
export type Dictionary<T, Id extends EntityId> = Partial<Record<Id, T>>
24-
2521
/**
2622
* @public
2723
*/
@@ -32,7 +28,7 @@ export type Update<T, Id extends EntityId> = { id: Id; changes: Partial<T> }
3228
*/
3329
export interface EntityState<T, Id extends EntityId> {
3430
ids: Id[]
35-
entities: Dictionary<T, Id>
31+
entities: Record<Id, T>
3632
}
3733

3834
/**
@@ -43,10 +39,9 @@ export interface EntityDefinition<T, Id extends EntityId> {
4339
sortComparer: false | Comparer<T>
4440
}
4541

46-
export type PreventAny<S, T, Id extends EntityId> = IsAny<
42+
export type PreventAny<S, T, Id extends EntityId> = CastAny<
4743
S,
48-
EntityState<T, Id>,
49-
S
44+
EntityState<T, Id>
5045
>
5146

5247
/**
@@ -158,10 +153,10 @@ export interface EntityStateAdapter<T, Id extends EntityId> {
158153
*/
159154
export interface EntitySelectors<T, V, Id extends EntityId> {
160155
selectIds: (state: V) => Id[]
161-
selectEntities: (state: V) => Dictionary<T, Id>
156+
selectEntities: (state: V) => Record<Id, T>
162157
selectAll: (state: V) => T[]
163158
selectTotal: (state: V) => number
164-
selectById: (state: V, id: Id) => T | undefined
159+
selectById: (state: V, id: Id) => Compute<UncheckedIndexedAccess<T>>
165160
}
166161

167162
/**

packages/toolkit/src/entities/sorted_state_adapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function createSortedStateAdapter<T, Id extends EntityId>(
6161
state: R
6262
): void {
6363
newEntities = ensureEntitiesArray(newEntities)
64-
state.entities = {}
64+
state.entities = {} as Record<Id, T>
6565
state.ids = []
6666

6767
addManyMutably(newEntities, state)

packages/toolkit/src/entities/state_selectors.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import type { CreateSelectorFunction, Selector } from 'reselect'
22
import { createDraftSafeSelector } from '../createDraftSafeSelector'
3-
import type {
4-
EntityState,
5-
EntitySelectors,
6-
Dictionary,
7-
EntityId,
8-
} from './models'
3+
import type { EntityState, EntitySelectors, EntityId } from './models'
94

105
export type AnyCreateSelectorFunction = CreateSelectorFunction<
116
(...args: unknown[]) => unknown,
@@ -42,7 +37,7 @@ export function createSelectorsFactory<T, Id extends EntityId>() {
4237

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

45-
const selectById = (entities: Dictionary<T, Id>, id: Id) => entities[id]
40+
const selectById = (entities: Record<Id, T>, id: Id) => entities[id]
4641

4742
const selectTotal = createSelector(selectIds, (ids) => ids.length)
4843

packages/toolkit/src/entities/unsorted_state_adapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export function createUnsortedStateAdapter<T, Id extends EntityId>(
6767
newEntities = ensureEntitiesArray(newEntities)
6868

6969
state.ids = []
70-
state.entities = {}
70+
state.entities = {} as Record<Id, T>
7171

7272
addManyMutably(newEntities, state)
7373
}

packages/toolkit/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ export { Tuple } from './utils'
108108

109109
export { createEntityAdapter } from './entities/create_adapter'
110110
export type {
111-
Dictionary,
112111
EntityState,
113112
EntityAdapter,
114113
EntitySelectors,

packages/toolkit/src/tests/configureStore.test.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -283,16 +283,28 @@ describe('configureStore', async () => {
283283
undefined,
284284
expect.any(Function)
285285
)
286+
287+
expect(dummyEnhancerCalled).toBe(true)
286288
})
287289

288-
it('accepts a callback for customizing enhancers', () => {
289-
const store = configureStore({
290-
reducer,
291-
enhancers: (getDefaultEnhancers) =>
292-
getDefaultEnhancers().concat(dummyEnhancer),
290+
describe('invalid arguments', () => {
291+
test('enhancers is not a callback', () => {
292+
expect(() => configureStore({ reducer, enhancers: [] as any })).toThrow(
293+
'"enhancers" field must be a callback'
294+
)
293295
})
294296

295-
expect(dummyEnhancerCalled).toBe(true)
297+
test('callback fails to return array', () => {
298+
expect(() =>
299+
configureStore({ reducer, enhancers: (() => {}) as any })
300+
).toThrow('"enhancers" callback must return an array')
301+
})
302+
303+
test('array contains non-function', () => {
304+
expect(() =>
305+
configureStore({ reducer, enhancers: (() => ['']) as any })
306+
).toThrow('each enhancer provided to configureStore must be a function')
307+
})
296308
})
297309

298310
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
@@ -306,20 +318,20 @@ describe('configureStore', async () => {
306318
it('warns if middleware enhancer is excluded from final array when middlewares are provided', () => {
307319
const store = configureStore({
308320
reducer,
309-
enhancers: new Tuple(dummyEnhancer),
321+
enhancers: () => new Tuple(dummyEnhancer),
310322
})
311323

312324
expect(dummyEnhancerCalled).toBe(true)
313325

314326
expect(consoleSpy).toHaveBeenCalledWith(
315-
'middlewares were provided, but middleware enhancer was not included in final enhancers'
327+
'middlewares were provided, but middleware enhancer was not included in final enhancers - make sure to call `getDefaultEnhancers`'
316328
)
317329
})
318330
it("doesn't warn when middleware enhancer is excluded if no middlewares provided", () => {
319331
const store = configureStore({
320332
reducer,
321333
middleware: new Tuple(),
322-
enhancers: new Tuple(dummyEnhancer),
334+
enhancers: () => new Tuple(dummyEnhancer),
323335
})
324336

325337
expect(dummyEnhancerCalled).toBe(true)

0 commit comments

Comments
 (0)