diff --git a/src/Updux.ts b/src/Updux.ts index d35b81c..b54ab15 100644 --- a/src/Updux.ts +++ b/src/Updux.ts @@ -26,10 +26,10 @@ type ResolveAction< ? ActionArg : ActionArg extends (...args: any) => any ? ActionCreatorWithPreparedPayload< - Parameters, - ReturnType, - ActionType - > + Parameters, + ReturnType, + ActionType + > : ActionCreatorWithoutPayload; type ResolveActions< @@ -37,10 +37,10 @@ type ResolveActions< [key: string]: any; }, > = { - [ActionType in keyof A]: ActionType extends string + [ActionType in keyof A]: ActionType extends string ? ResolveAction : never; -}; + }; export type Mutation = Action, S = any> = ( payload: A extends { @@ -51,12 +51,22 @@ export type Mutation = Action, S = any> = ( action: A, ) => (state: S) => S | void; +export type AggregateSelectors = S; + +type SelectorForState = (state: S) => unknown; +type SelectorsForState = { + [key: string]: SelectorForState; +}; + export default class Updux< T_LocalState = Record, T_LocalActions extends { [actionType: string]: any; } = {}, T_Subduxes extends Record = {}, + T_LocalSelectors extends SelectorsForState< + AggregateState + > = {}, > { #localInitial: T_LocalState; #localActions: T_LocalActions; @@ -70,11 +80,19 @@ export default class Updux< #initial: AggregateState; + #localSelectors: Record< + string, + (state: AggregateState) => any + >; + + #selectors: AggregateSelectors; + constructor( config: Partial<{ initial: T_LocalState; actions: T_LocalActions; subduxes: T_Subduxes; + selectors: T_LocalSelectors; }>, ) { // TODO check that we can't alter the initial after the fact @@ -85,6 +103,7 @@ export default class Updux< this.#actions = buildActions(this.#localActions, this.#subduxes); this.#initial = buildInitial(this.#localInitial, this.#subduxes); + this.#localSelectors = config.selectors; } get actions() { @@ -110,6 +129,10 @@ export default class Updux< return store; } + get selectors(): T_LocalSelectors { + return this.#localSelectors as any; + } + // TODO memoize this sucker get reducer() { return buildReducer( diff --git a/src/selectors.test.ts b/src/selectors.test.ts new file mode 100644 index 0000000..86430c3 --- /dev/null +++ b/src/selectors.test.ts @@ -0,0 +1,27 @@ +import { test, expect } from 'vitest'; + +import Updux, { createAction } from './index.js'; + +test('basic selectors', () => { + type State = { x: number }; + + const foo = new Updux({ + initial: { + x: 1, + }, + selectors: { + getX: ({ x }: State) => x, + }, + subduxes: { + bar: new Updux({ + initial: { y: 2 }, + selectors: { + getY: ({ y }: { y: number }) => y, + }, + }), + }, + }); + + expect(foo.selectors.getY({ bar: { y: 3 } } as typeof foo.initial)).toBe(3); + expect(foo.selectors.getX({ x: 4 } as typeof foo.initial)).toBe(4); +});