Skip to content

Commit

Permalink
fix: improve typings for deps/inject and action payload
Browse files Browse the repository at this point in the history
  • Loading branch information
Temzasse committed Jan 17, 2019
1 parent 7f3dc9c commit 66e4be1
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 30 deletions.
2 changes: 1 addition & 1 deletion example/typed/src/components/order/order.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const model = createModel<State, Actions, Deps>({
},
actions: ({ initialState }) => ({
// Basic actions
fooAction: (state, action: { payload: number }) => ({
fooAction: (state, action) => ({
...state,
foo: action.payload,
}),
Expand Down
58 changes: 34 additions & 24 deletions example/typed/typings/reducktion/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,77 @@
declare module 'reducktion' {
// Helper
type ArgumentType<F extends Function> = F extends (arg: infer A) => any
? A
: never;

// TODO: do we need this?
interface RootState<StatePart> {
[statePart: string]: StatePart;
}

type Selector<State> = (state: RootState<State>, ...args: any[]) => any;

// Provide action keys for auto-complete but allow custom types
// that are eg. auto-generated by fetchable action
type ActionTypes<Actions> = { [K in keyof Actions]: string } & {
[x: string]: string;
};
interface ActionCreator<Payload = any> {

interface ActionCreator<Payload> {
type: string;
payload: Payload;
[x: string]: any; // Allow additional meta fields
}

type ActionFunc<Payload = any> = (
payload?: Payload
) => ActionCreator<Payload>;

type Thunk<Deps> = (
arg: any,
deps: Deps
) => (dispatch: any, getState: () => any, ...args: any[]) => Promise<void>;

type Reducer<State, Payload = any> = (
state: State,
action: ActionCreator<Payload>
) => State;

interface FetchableReducers<State> {
loading: Reducer<State>;
success: Reducer<State>;
failure: Reducer<State>;
}

interface Fetchable {
value: <T>(val: T) => FetchableValue<T>,
value: <T>(val: T) => FetchableValue<T>;
action: <State, K extends keyof State>(
// Only allow state fields for fetchable values
stateField: FetchableValue extends State[K] ? K : never,
customReducers?: Partial<FetchableReducers<State>>
) => FetchableReducers<State>,
) => FetchableReducers<State>;
}

interface Dependencies {
[depName: string]: Model<any, any>;
}

// TODO:
// Figure out how to show proper error
// if given action is not in keyof Actions
interface ModelDefinition<State, Actions, Deps> {
name: string;
inject?: string[];
inject?: [keyof Deps];
state: State;
actions: (
{ initialState }: { initialState: State }
) => {
// Only include those keys that are present in the action's interface
[K in keyof Actions]: Reducer<State> | FetchableReducers<State>
[K in keyof Actions]: Actions[K] extends FetchableAction<any>
? FetchableReducers<State>
: Actions[K] extends Function
? Reducer<State, ArgumentType<Actions[K]>>
: never
};
reactions?: (
{ initialState, deps }: { initialState: State; deps: Deps }
Expand All @@ -81,7 +91,7 @@ declare module 'reducktion' {
[thunkName: string]: Thunk<Deps>;
};
}

interface Model<State, Actions> {
name: string;
initialState: State;
Expand All @@ -95,34 +105,34 @@ declare module 'reducktion' {
getSagas: () => [];
getReducer: () => Reducer<any>;
}

// EXPORTS ********************************************************************

export enum FetchableStatus {
INITIAL,
LOADING,
SUCCESS,
FAILURE,
}

export interface FetchableValue<Data = any> {
data: Data;
error: any;
status: FetchableStatus;
}

export interface FetchableAction<SuccessData> extends ActionFunc {
init: ActionFunc;
fail: ActionFunc;
success: ActionFunc<SuccessData>;
}

export const fetchable: Fetchable;

export function createModel<State, Actions, Deps = Dependencies>(
df: ModelDefinition<State, Actions, Deps>
): Model<State, Actions>;

export function initModels(
models: Model<any, any>[]
): {
Expand All @@ -132,5 +142,5 @@ declare module 'reducktion' {
allSagas: any[];
} & {
[modelName: string]: Model<any, any>;
};
};
}
20 changes: 15 additions & 5 deletions src/reducktion.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Helper
type ArgumentType<F extends Function> = F extends (arg: infer A) => any
? A
: never;

// TODO: do we need this?
interface RootState<StatePart> {
[statePart: string]: StatePart;
}
Expand All @@ -10,7 +16,7 @@ type ActionTypes<Actions> = { [K in keyof Actions]: string } & {
[x: string]: string;
};

interface ActionCreator<Payload = any> {
interface ActionCreator<Payload> {
type: string;
payload: Payload;
[x: string]: any; // Allow additional meta fields
Expand All @@ -37,12 +43,12 @@ interface FetchableReducers<State> {
}

interface Fetchable {
value: <T>(val: T) => FetchableValue<T>,
value: <T>(val: T) => FetchableValue<T>;
action: <State, K extends keyof State>(
// Only allow state fields for fetchable values
stateField: FetchableValue extends State[K] ? K : never,
customReducers?: Partial<FetchableReducers<State>>
) => FetchableReducers<State>,
) => FetchableReducers<State>;
}

interface Dependencies {
Expand All @@ -54,13 +60,17 @@ interface Dependencies {
// if given action is not in keyof Actions
interface ModelDefinition<State, Actions, Deps> {
name: string;
inject?: string[];
inject?: [keyof Deps];
state: State;
actions: (
{ initialState }: { initialState: State }
) => {
// Only include those keys that are present in the action's interface
[K in keyof Actions]: Reducer<State> | FetchableReducers<State>
[K in keyof Actions]: Actions[K] extends FetchableAction<any>
? FetchableReducers<State>
: Actions[K] extends Function
? Reducer<State, ArgumentType<Actions[K]>>
: never
};
reactions?: (
{ initialState, deps }: { initialState: State; deps: Deps }
Expand Down

0 comments on commit 66e4be1

Please sign in to comment.