From 14700203a4c18ec5f7739dc7478e7508069ad9fb Mon Sep 17 00:00:00 2001 From: Daniel Lytkin Date: Mon, 1 Feb 2016 15:00:00 +0600 Subject: [PATCH] Add type definitions for redux-saga --- redux-saga/redux-saga-tests.ts | 172 +++++++++++++++++++++++ redux-saga/redux-saga-tests.ts.tscparams | 1 + redux-saga/redux-saga.d.ts | 110 +++++++++++++++ redux-saga/redux-saga.d.ts.tscparams | 1 + 4 files changed, 284 insertions(+) create mode 100644 redux-saga/redux-saga-tests.ts create mode 100644 redux-saga/redux-saga-tests.ts.tscparams create mode 100644 redux-saga/redux-saga.d.ts create mode 100644 redux-saga/redux-saga.d.ts.tscparams diff --git a/redux-saga/redux-saga-tests.ts b/redux-saga/redux-saga-tests.ts new file mode 100644 index 000000000..933a397b1 --- /dev/null +++ b/redux-saga/redux-saga-tests.ts @@ -0,0 +1,172 @@ +/// + + +import sagaMiddleware, { + take, + put, + race, + call, + fork, + cancel, + storeIO, + runSaga, + Saga, + SagaCancellationException +} from 'redux-saga' +import {applyMiddleware, createStore} from 'redux'; + +declare const delay: (ms: number) => Promise; +declare const fetchApi: (url: string) => Promise; + +namespace GettingStarted { + + const incrementAsync:Saga = function* incrementAsync() { + + while(true) { + + // wait for each INCREMENT_ASYNC action + const nextAction = yield take('INCREMENT_ASYNC') + + // delay is a sample function + // return a Promise that resolves after (ms) milliseconds + yield delay(1000) + + // dispatch INCREMENT_COUNTER + yield put( {type: 'INCREMENT_COUNTER'} ) + } + + } + + const createStoreWithSaga = applyMiddleware( + // ..., + sagaMiddleware(incrementAsync) + )(createStore) + + export default function configureStore(initialState) { + return createStoreWithSaga((state: any) => state, initialState) + } +} + + +namespace EffectCombinators { + const fetchPostsWithTimeout:Saga = function* fetchPostsWithTimeout() { + while( yield take('FETCH_POSTS') ) { + // starts a race between 2 effects + const {posts, timeout} = yield race({ + posts : call(fetchApi, '/posts'), + timeout : call(delay, 1000) + }) + + if(posts) + put( {type: 'RECEIVE_POSTS', posts} ) + else + put( {type: 'TIMEOUT_ERROR'} ) + } + } +} + + +namespace SequencingSagasViaYield { + function showScore(score) { + return { + type: 'SHOW_SCORE', score + } + } + + function* playLevelOne(getState) { yield 1 } + + function* playLevelTwo(getState) { yield 2 } + + function* playLevelThree(getState) { yield 3 } + + const game: Saga = function* game(getState) { + + const score1 = yield* playLevelOne(getState) + yield put(showScore(score1)) + + const score2 = yield* playLevelTwo(getState) + yield put(showScore(score2)) + + const score3 = yield* playLevelThree(getState) + yield put(showScore(score3)) + + } +} + + +namespace ComposingSagas { + function* fetchProducts() { + yield put( {type: 'REQUEST_PRODUCTS'} ) + const products = yield call(fetchApi, '/products') + yield put( {type: 'RECEIVE_PRODUCTS', products } ) + } + + function* watchFetch() { + while ( yield take('FETCH_PRODUCTS') ) { + yield call(fetchProducts) // waits for the fetchProducts task to + // terminate + } + } +} + + +namespace NonBlockingCallsWithForkJoin { + function* fetchPosts() { + yield put( {type: 'REQUEST_POSTS'} ) + const posts = yield call(fetchApi, '/posts') + yield put( {type: 'RECEIVE_POSTS', posts} ) + } + + function* watchFetch() { + while ( yield take('FETCH_POSTS') ) { + yield fork(fetchPosts) // non blocking call + } + } +} + + +namespace TaskCancellation { + declare const someApi: () => any; + + function* bgSync() { + try { + while(true) { + yield put({type: 'REQUEST_START'}) + const result = yield call(someApi) + yield put({type: 'REQUEST_SUCCESS', result}) + yield call(delay, 5000) + } + } catch(error) { + if(error instanceof SagaCancellationException) + yield put({type: 'REQUEST_FAILURE', message: 'Sync cancelled!'}) + } + } + + function* main() { + while( yield take('START_BACKGROUND_SYNC') ) { + // starts the task in the background + const bgSyncTask = yield fork(bgSync) + + // wait for the user stop action + yield take('STOP_BACKGROUND_SYNC') + // user clicked stop. cancel the background task + // this will throw a SagaCancellationException into the forked bgSync + // task + yield cancel(bgSyncTask) + } + } +} + + +namespace DynamicallyStartingSagasWithRunSaga { + const store = createStore((state: any, action: any) => state); + + function* serverSaga(getState) { + yield getState() + } + + runSaga( + serverSaga(store.getState), + storeIO(store) + ) +} diff --git a/redux-saga/redux-saga-tests.ts.tscparams b/redux-saga/redux-saga-tests.ts.tscparams new file mode 100644 index 000000000..14fce22a5 --- /dev/null +++ b/redux-saga/redux-saga-tests.ts.tscparams @@ -0,0 +1 @@ +--target ES6 diff --git a/redux-saga/redux-saga.d.ts b/redux-saga/redux-saga.d.ts new file mode 100644 index 000000000..6573f1245 --- /dev/null +++ b/redux-saga/redux-saga.d.ts @@ -0,0 +1,110 @@ +// Type definitions for redux-saga 0.6.0 +// Project: https://github.com/yelouafi/redux-saga +// Definitions by: Daniel Lytkin +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// + +declare module 'redux-saga' { + export class SagaCancellationException { + } + + export type Effect = {}; + + export type Saga = (getState?: () => T) => Iterable; + + type Predicate = (action: any) => boolean; + + export function take(pattern?: string|string[]|Predicate): Effect; + + export function put(action: any): Effect; + + export function race(effects: {[key:string]: any}): Effect; + + export function call(fn: (arg1?: T1, arg2?: T2, arg3?: T3, ...rest: any[]) => any, + arg1?: T1, arg2?: T2, arg3?: T3, ...rest: any[]): Effect; + + + export interface Task { + name:string; + isRunning():boolean; + result():T; + error():any; + } + + export function fork(effect: Effect): Effect; + export function fork(fn: (arg1?: T1, arg2?: T2, arg3?: T3, ...rest: any[]) => + Promise|Iterable, + arg1?: T1, arg2?: T2, arg3?: T3, ...rest: any[]): Effect; + + export function join(task: Task): Effect; + + export function cancel(task: Task): Effect; + + + import {Middleware} from 'redux'; + export default function (...sagas: Saga[]): Middleware; + + export { + CANCEL, + RACE_AUTO_CANCEL, + PARALLEL_AUTO_CANCEL, + MANUAL_CANCEL + } from 'redux-saga/lib/proc'; + + import * as monitorActions from 'redux-saga/lib/monitorActions'; + export {monitorActions} + + export {runSaga, storeIO} from 'redux-saga/lib/runSaga' + +} + + +declare module 'redux-saga/lib/proc' { + import {Task} from 'redux-saga'; + + export const CANCEL: symbol; + export const NOT_ITERATOR_ERROR: string; + export const PARALLEL_AUTO_CANCEL: string; + export const RACE_AUTO_CANCEL: string; + export const MANUAL_CANCEL: string; + + export default function proc(iterator: Iterable, + subscribe?: (cb: Function) => Function, + dispatch?: (action: any) => any, + monitor?: (action: any) => void, + parentEffectId?: any, + name?: string): Task; +} + + +declare module 'redux-saga/lib/runSaga' { + import {Store} from 'redux'; + import {Task} from 'redux-saga'; + + interface IO { + dispatch: (action: any) => any; + subscribe: (cb: Function) => Function; + } + + export function storeIO(store: Store): IO; + + export function runSaga(iterator: Iterable, + io: IO, + monitor?: (action: any) => void): Task; +} + + +declare module 'redux-saga/lib/emitter' { + export default function emitter(): { + subscribe(cb: Function):Function; + emit(item: any):void; + } +} + +declare module 'redux-saga/lib/monitorActions' { + export const MONITOR_ACTION: string; + export const EFFECT_TRIGGERED: string; + export const EFFECT_RESOLVED: string; + export const EFFECT_REJECTED: string; +} diff --git a/redux-saga/redux-saga.d.ts.tscparams b/redux-saga/redux-saga.d.ts.tscparams new file mode 100644 index 000000000..14fce22a5 --- /dev/null +++ b/redux-saga/redux-saga.d.ts.tscparams @@ -0,0 +1 @@ +--target ES6