From fcee65137575592a57f159c73e94184764724a1a Mon Sep 17 00:00:00 2001 From: Brandon Roberts Date: Thu, 28 Sep 2017 22:16:01 -0500 Subject: [PATCH] feat(Entity): Add support for string or number type for ID --- modules/entity/src/entity_state.ts | 2 +- modules/entity/src/models.ts | 61 +++++++++++++++++--- modules/entity/src/sorted_state_adapter.ts | 28 +++++---- modules/entity/src/state_adapter.ts | 7 ++- modules/entity/src/state_selectors.ts | 7 ++- modules/entity/src/unsorted_state_adapter.ts | 36 ++++++++---- 6 files changed, 107 insertions(+), 34 deletions(-) diff --git a/modules/entity/src/entity_state.ts b/modules/entity/src/entity_state.ts index aab8653b49..2f86ed2b62 100644 --- a/modules/entity/src/entity_state.ts +++ b/modules/entity/src/entity_state.ts @@ -1,4 +1,4 @@ -import { EntityState } from './models'; +import { EntityState, EntityStateStr, EntityStateNum } from './models'; export function getInitialEntityState(): EntityState { return { diff --git a/modules/entity/src/models.ts b/modules/entity/src/models.ts index 40f18929de..c9656b41fa 100644 --- a/modules/entity/src/models.ts +++ b/modules/entity/src/models.ts @@ -1,25 +1,57 @@ -export type Comparer = { +export type ComparerStr = { + (a: T, b: T): string; +}; + +export type ComparerNum = { (a: T, b: T): number; }; -export type IdSelector = { +export type Comparer = ComparerNum | ComparerStr; + +export type IdSelectorStr = { (model: T): string; }; -export type Dictionary = { +export type IdSelectorNum = { + (model: T): number; +}; + +export type IdSelector = IdSelectorStr | IdSelectorNum; + +export type DictionaryStr = { [id: string]: T; }; -export type Update = { +export type DictionaryNum = { + [id: number]: T; +}; + +export type Dictionary = DictionaryStr | DictionaryNum; + +export type UpdateStr = { id: string; changes: Partial; }; -export interface EntityState { +export type UpdateNum = { + id: number; + changes: Partial; +}; + +export type Update = UpdateStr | UpdateNum; + +export interface EntityStateStr { ids: string[]; entities: Dictionary; } +export interface EntityStateNum { + ids: number[]; + entities: Dictionary; +} + +export type EntityState = EntityStateStr | EntityStateNum; + export interface EntityDefinition { selectId: IdSelector; sortComparer: false | Comparer; @@ -31,20 +63,35 @@ export interface EntityStateAdapter { addAll>(entities: T[], state: S): S; removeOne>(key: string, state: S): S; + removeOne>(key: number, state: S): S; + removeMany>(keys: string[], state: S): S; + removeMany>(keys: number[], state: S): S; + removeAll>(state: S): S; updateOne>(update: Update, state: S): S; updateMany>(updates: Update[], state: S): S; } -export type EntitySelectors = { - selectIds: (state: V) => string[]; +export type EntitySelectorsBase = { selectEntities: (state: V) => Dictionary; selectAll: (state: V) => T[]; selectTotal: (state: V) => number; }; +export interface EntitySelectorsStr extends EntitySelectorsBase { + selectIds: (state: V) => string[]; +} + +export interface EntitySelectorsNum extends EntitySelectorsBase { + selectIds: (state: V) => number[]; +} + +export type EntitySelectors = + | EntitySelectorsNum + | EntitySelectorsStr; + export interface EntityAdapter extends EntityStateAdapter { getInitialState(): EntityState; getInitialState(state: S): EntityState & S; diff --git a/modules/entity/src/sorted_state_adapter.ts b/modules/entity/src/sorted_state_adapter.ts index ad95428e52..4ae8e694b6 100644 --- a/modules/entity/src/sorted_state_adapter.ts +++ b/modules/entity/src/sorted_state_adapter.ts @@ -12,18 +12,21 @@ import { createUnsortedStateAdapter } from './unsorted_state_adapter'; export function createSortedStateAdapter( selectId: IdSelector, sort: Comparer -): EntityStateAdapter { +): EntityStateAdapter; +export function createSortedStateAdapter(selectId: any, sort: any): any { type R = EntityState; const { removeOne, removeMany, removeAll } = createUnsortedStateAdapter( selectId ); - function addOneMutably(entity: T, state: R): boolean { + function addOneMutably(entity: T, state: R): boolean; + function addOneMutably(entity: any, state: any): boolean { return addManyMutably([entity], state); } - function addManyMutably(newModels: T[], state: R): boolean { + function addManyMutably(newModels: T[], state: R): boolean; + function addManyMutably(newModels: any[], state: any): boolean { const models = newModels.filter( model => !(selectId(model) in state.entities) ); @@ -31,7 +34,8 @@ export function createSortedStateAdapter( return merge(models, state); } - function addAllMutably(models: T[], state: R): boolean { + function addAllMutably(models: T[], state: R): boolean; + function addAllMutably(models: any[], state: any): boolean { state.entities = {}; state.ids = []; @@ -40,11 +44,13 @@ export function createSortedStateAdapter( return true; } - function updateOneMutably(update: Update, state: R): boolean { + function updateOneMutably(update: Update, state: R): boolean; + function updateOneMutably(update: any, state: any): boolean { return updateManyMutably([update], state); } - function takeUpdatedModel(models: T[], update: Update, state: R): void { + function takeUpdatedModel(models: T[], update: Update, state: R): void; + function takeUpdatedModel(models: any[], update: any, state: any): void { if (!(update.id in state.entities)) { return; } @@ -57,26 +63,28 @@ export function createSortedStateAdapter( models.push(updated); } - function updateManyMutably(updates: Update[], state: R): boolean { + function updateManyMutably(updates: Update[], state: R): boolean; + function updateManyMutably(updates: any[], state: any): boolean { const models: T[] = []; updates.forEach(update => takeUpdatedModel(models, update, state)); if (models.length) { - state.ids = state.ids.filter(id => id in state.entities); + state.ids = state.ids.filter((id: any) => id in state.entities); } return merge(models, state); } - function merge(models: T[], state: R): boolean { + function merge(models: T[], state: R): boolean; + function merge(models: any[], state: any): boolean { if (models.length === 0) { return false; } models.sort(sort); - const ids: string[] = []; + const ids: any[] = []; let i = 0; let j = 0; diff --git a/modules/entity/src/state_adapter.ts b/modules/entity/src/state_adapter.ts index 9a0d6c68be..bf1fd7da77 100644 --- a/modules/entity/src/state_adapter.ts +++ b/modules/entity/src/state_adapter.ts @@ -2,8 +2,11 @@ import { EntityState, EntityStateAdapter } from './models'; export function createStateOperator( mutator: (arg: R, state: EntityState) => boolean -) { - return function operation>(arg: R, state: S): S { +): EntityState; +export function createStateOperator( + mutator: (arg: any, state: any) => boolean +): any { + return function operation>(arg: R, state: any): S { const clonedEntityState: EntityState = { ids: [...state.ids], entities: { ...state.entities }, diff --git a/modules/entity/src/state_selectors.ts b/modules/entity/src/state_selectors.ts index 260edfb91e..8d8f1affdc 100644 --- a/modules/entity/src/state_selectors.ts +++ b/modules/entity/src/state_selectors.ts @@ -1,17 +1,18 @@ import { createSelector } from '@ngrx/store'; -import { EntityState, EntitySelectors } from './models'; +import { EntityState, EntitySelectors, Dictionary } from './models'; export function createSelectorsFactory() { return { getSelectors( selectState: (state: V) => EntityState ): EntitySelectors { - const selectIds = (state: EntityState) => state.ids; + const selectIds = (state: any) => state.ids; const selectEntities = (state: EntityState) => state.entities; const selectAll = createSelector( selectIds, selectEntities, - (ids, entities) => ids.map(id => entities[id]) + (ids: T[], entities: Dictionary): any => + ids.map((id: any) => (entities as any)[id]) ); const selectTotal = createSelector(selectIds, ids => ids.length); diff --git a/modules/entity/src/unsorted_state_adapter.ts b/modules/entity/src/unsorted_state_adapter.ts index 5f23f2d8e9..37057137ab 100644 --- a/modules/entity/src/unsorted_state_adapter.ts +++ b/modules/entity/src/unsorted_state_adapter.ts @@ -3,10 +3,12 @@ import { createStateOperator } from './state_adapter'; export function createUnsortedStateAdapter( selectId: IdSelector -): EntityStateAdapter { +): EntityStateAdapter; +export function createUnsortedStateAdapter(selectId: IdSelector): any { type R = EntityState; - function addOneMutably(entity: T, state: R): boolean { + function addOneMutably(entity: T, state: R): boolean; + function addOneMutably(entity: any, state: any): boolean { const key = selectId(entity); if (key in state.entities) { @@ -19,7 +21,8 @@ export function createUnsortedStateAdapter( return true; } - function addManyMutably(entities: T[], state: R): boolean { + function addManyMutably(entities: T[], state: R): boolean; + function addManyMutably(entities: any[], state: any): boolean { let didMutate = false; for (let index in entities) { @@ -29,7 +32,8 @@ export function createUnsortedStateAdapter( return didMutate; } - function addAllMutably(entities: T[], state: R): boolean { + function addAllMutably(entities: T[], state: R): boolean; + function addAllMutably(entities: any[], state: any): boolean { state.ids = []; state.entities = {}; @@ -38,24 +42,27 @@ export function createUnsortedStateAdapter( return true; } - function removeOneMutably(key: string, state: R): boolean { + function removeOneMutably(key: T, state: R): boolean; + function removeOneMutably(key: any, state: any): boolean { return removeManyMutably([key], state); } - function removeManyMutably(keys: string[], state: R): boolean { + function removeManyMutably(keys: T[], state: R): boolean; + function removeManyMutably(keys: any[], state: any): boolean { const didMutate = keys .filter(key => key in state.entities) .map(key => delete state.entities[key]).length > 0; if (didMutate) { - state.ids = state.ids.filter(id => id in state.entities); + state.ids = state.ids.filter((id: any) => id in state.entities); } return didMutate; } - function removeAll(state: S): S { + function removeAll(state: S): S; + function removeAll(state: any): S { return Object.assign({}, state, { ids: [], entities: {}, @@ -66,6 +73,11 @@ export function createUnsortedStateAdapter( keys: { [id: string]: string }, update: Update, state: R + ): void; + function takeNewKey( + keys: { [id: string]: any }, + update: Update, + state: any ): void { const original = state.entities[update.id]; const updated: T = Object.assign({}, original, update.changes); @@ -79,11 +91,13 @@ export function createUnsortedStateAdapter( state.entities[newKey] = updated; } - function updateOneMutably(update: Update, state: R): boolean { + function updateOneMutably(update: Update, state: R): boolean; + function updateOneMutably(update: any, state: any): boolean { return updateManyMutably([update], state); } - function updateManyMutably(updates: Update[], state: R): boolean { + function updateManyMutably(updates: Update[], state: R): boolean; + function updateManyMutably(updates: any[], state: any): boolean { const newKeys: { [id: string]: string } = {}; const didMutate = @@ -92,7 +106,7 @@ export function createUnsortedStateAdapter( .map(update => takeNewKey(newKeys, update, state)).length > 0; if (didMutate) { - state.ids = state.ids.map(id => newKeys[id] || id); + state.ids = state.ids.map((id: any) => newKeys[id] || id); } return didMutate;