Permalink
Browse files

feat(typings): Updating typings for more common use-cases.

This is a first draft / suggestion for a possible reordering of typings re: #446 that accomplishes the following goals:

* Generic types for Epics are reordered to InputActions, OutputActions, State, Dependencies
* OutputActions now extend from Action instead of T allowing for more specific declarations for input and output actions
* State is made optional and defaults to 'void' for cases where it would be unused. This requires an explicit type for the generic if it's to be used
* Added a type for combineEpics that takes no generic arguments. It is common to have epics with different type signatures, and it would be very difficult / impossible to enforce type safety through generics for combineEpics at this level.

Tests have also been updated to use the new generic type ordering
  • Loading branch information...
ajcrites committed Apr 10, 2018
1 parent 3c9130d commit 0b0efc05ed7fb6f70efa3faa0e106a9a4384069c
Showing with 23 additions and 21 deletions.
  1. +6 −5 index.d.ts
  2. +17 −16 test/typings.ts
@@ -38,12 +38,12 @@ export declare class StateObservable<S> extends Observable<S> {
}


export declare interface Epic<T extends Action, S, D = any, O extends T = T> {
export declare interface Epic<T extends Action, O extends Action = T, S = void, D = any> {
(action$: ActionsObservable<T>, state$: StateObservable<S>, dependencies: D): Observable<O>;
}

export interface EpicMiddleware<T extends Action, S, D = any, O extends T = T> extends Middleware {
replaceEpic(nextEpic: Epic<T, S, D, O>): void;
export interface EpicMiddleware<T extends Action, O extends Action = T, S = void, D = any> extends Middleware {
replaceEpic(nextEpic: Epic<T, O, S, D>): void;
}

interface Adapter {
@@ -56,10 +56,11 @@ interface Options<D = any> {
dependencies?: D;
}

export declare function createEpicMiddleware<T extends Action, S, D = any, O extends T = T>(rootEpic: Epic<T, S, D, O>, options?: Options<D>): EpicMiddleware<T, S, D, O>;
export declare function createEpicMiddleware<T extends Action, O extends Action = T, S = void, D = any>(rootEpic: Epic<T, O, S, D>, options?: Options<D>): EpicMiddleware<T, O, S, D>;

export declare function combineEpics<T extends Action, S, D = any, O extends T = T>(...epics: Epic<T, S, D, O>[]): Epic<T, S, D, O>;
export declare function combineEpics<T extends Action, O extends Action = T, S = void, D = any>(...epics: Epic<T, O, S, D>[]): Epic<T, O, S, D>;
export declare function combineEpics<E>(...epics: E[]): E;
export declare function combineEpics(...epics: any[]): any;

export declare function ofType<T extends Action, R extends T = T, K extends R['type'] = R['type']>(...key: K[]): (source: Observable<T>) => Observable<R>;

@@ -5,7 +5,7 @@ import { ajax } from 'rxjs/ajax';
import { map, mapTo, mergeMap } from 'rxjs/operators';

import { createEpicMiddleware, Epic, combineEpics,
EpicMiddleware, ActionsObservable, ofType } from '../';
EpicMiddleware, ActionsObservable, StateObservable, ofType } from '../';

interface State {
foo: string
@@ -22,23 +22,23 @@ interface Dependencies {
func(value: string): string;
}

const epic1: Epic<FluxStandardAction, State> = (action$, store) =>
const epic1: Epic<FluxStandardAction, FluxStandardAction, State> = (action$, store$) =>
action$.pipe(
ofType('FIRST'),
mapTo({
type: 'first',
payload: store.getState()
payload: store$.getState()
})
);

const epic2: Epic<FluxStandardAction, State> = (action$, store) =>
const epic2: Epic<FluxStandardAction> = action$ =>
action$.pipe(
ofType('SECOND', 'NEVER'),
mapTo('second'),
mergeMap(type => of({ type }))
);

const epic3: Epic<FluxStandardAction, State> = action$ =>
const epic3: Epic<FluxStandardAction> = action$ =>
action$.pipe(
ofType('THIRD'),
mapTo({
@@ -47,12 +47,12 @@ const epic3: Epic<FluxStandardAction, State> = action$ =>
);


const epic4: Epic<FluxStandardAction, State> = () =>
const epic4: Epic<FluxStandardAction> = () =>
of({
type: 'fourth'
});

const epic5: Epic<FluxStandardAction, State> = (action$, store) =>
const epic5: Epic<FluxStandardAction> = action$ =>
action$.pipe(
ofType('FIFTH'),
mergeMap(({ type, payload }) => of({
@@ -61,7 +61,7 @@ const epic5: Epic<FluxStandardAction, State> = (action$, store) =>
}))
);

const epic6: Epic<FluxStandardAction, State> = (action$, store) =>
const epic6: Epic<FluxStandardAction> = action$ =>
action$.pipe(
ofType('SIXTH'),
map(({ type, payload }) => ({
@@ -70,7 +70,7 @@ const epic6: Epic<FluxStandardAction, State> = (action$, store) =>
}))
);

const epic7: Epic<FluxStandardAction, State, Dependencies> = (action$, store, dependencies) =>
const epic7: Epic<FluxStandardAction, FluxStandardAction, State, Dependencies> = (action$, _, dependencies) =>
action$.pipe(
ofType('SEVENTH'),
map(({ type, payload }) => ({
@@ -79,7 +79,7 @@ const epic7: Epic<FluxStandardAction, State, Dependencies> = (action$, store, de
}))
);

const epic8: Epic<FluxStandardAction, State, Dependencies> = (action$, store, dependencies) =>
const epic8: Epic<FluxStandardAction, FluxStandardAction, State, Dependencies> = (action$, store, dependencies) =>
action$.pipe(
ofType('EIGHTH'),
map(({ type, payload }) => ({
@@ -97,34 +97,35 @@ interface Epic9_Output {
payload: string,
}

const epic9_1: Epic<FluxStandardAction, State, Dependencies, Epic9_Output> = (action$, store, dependencies) =>
const epic9_1: Epic<FluxStandardAction, Epic9_Output, State, Dependencies> = (action$, store, dependencies) =>
action$.pipe(
ofType<FluxStandardAction, Epic9_Input>('NINTH'),
map(({ type, payload }) => ({
type: 'ninth' as 'ninth',
payload: dependencies.func("ninth-" + payload),
}))
);
const epic9_2 = (action$: ActionsObservable<FluxStandardAction>, store: MiddlewareAPI<State>, dependencies: Dependencies) =>

const epic9_2 = (action$: ActionsObservable<FluxStandardAction>, store: StateObservable<void>, dependencies: Dependencies) =>
action$.pipe(
ofType<FluxStandardAction, Epic9_Input>('NINTH'),
map(({ type, payload }) => ({
type: 'ninth',
payload: dependencies.func("ninth-" + payload),
} as Epic9_Output))
);
const rootEpic1: Epic<FluxStandardAction, State> = combineEpics<FluxStandardAction, State>(epic1, epic2, epic3, epic4, epic5, epic6, epic7, epic8, epic9_1);
const rootEpic1: Epic<FluxStandardAction> = combineEpics<FluxStandardAction, FluxStandardAction, any>(epic1, epic2, epic3, epic4, epic5, epic6, epic7, epic8, epic9_1);
const rootEpic2 = combineEpics(epic1, epic2, epic3, epic4, epic5, epic6, epic7, epic8, epic9_2);

const dependencies: Dependencies = {
func(value: string) { return `func-${value}`}
};

const epicMiddleware1: EpicMiddleware<FluxStandardAction, State> = createEpicMiddleware<FluxStandardAction, State>(rootEpic1, { dependencies });
const epicMiddleware1: EpicMiddleware<FluxStandardAction> = createEpicMiddleware<FluxStandardAction>(rootEpic1, { dependencies });
const epicMiddleware2 = createEpicMiddleware(rootEpic2, { dependencies });

interface CustomEpic<T extends Action, S, U> {
(action$: ActionsObservable<T>, store: MiddlewareAPI<S>, api: U): Observable<T>;
(action$: ActionsObservable<T>, store: StateObservable<S>, api: U): Observable<T>;
}

const customEpic: CustomEpic<FluxStandardAction, State, number> = (action$, store, some) =>
@@ -145,7 +146,7 @@ const customEpic2: CustomEpic<FluxStandardAction, State, number> = (action$, sto
}))
);

const customEpicMiddleware: EpicMiddleware<FluxStandardAction, State> = createEpicMiddleware<FluxStandardAction, State>(rootEpic1, {
const customEpicMiddleware: EpicMiddleware<FluxStandardAction> = createEpicMiddleware<FluxStandardAction>(rootEpic1, {
dependencies: { getJSON: ajax.getJSON }
});

0 comments on commit 0b0efc0

Please sign in to comment.