diff --git a/definitions/action.d.ts b/definitions/action.d.ts new file mode 100644 index 0000000..bc96ab4 --- /dev/null +++ b/definitions/action.d.ts @@ -0,0 +1,73 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary Definitions for redux-logic + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +// +// ACTION +// + +/* * * * * * + | The following definitions are bascially identical to | + | flux-standard-action without an extra package. It also | + | makes use of conditional type to make the type of | + | payload and meta more accurate. | + * * * * * */ + +/** Action as an agrument */ +export type ArgumentAction< + Type extends string = string, + Payload extends object = undefined, + Meta extends object = undefined +> = ActionBasis & Partial>; + +/** all different types of Action */ +export type Action< + Type extends string = string, + Payload extends object = undefined, + Meta extends object = undefined +> = + | ErroneousAction + | (StandardAction & { error?: false }); + +/** Action without any error */ +export type StandardAction< + Type extends string = string, + Payload extends object = undefined, + Meta extends object = undefined +> = ActionBasis & PayloadBasis & MetaBasis; + +/** Action with an Error */ +export type ErroneousAction< + Type extends string = string, + Meta extends object = undefined +> = ActionBasis & PayloadBasis & MetaBasis & { error: true }; + +/* ----- Auxiliary Types ----- */ + +/** the most basic action object */ +export interface ActionBasis { + type: Type extends infer U ? U : string; +} + +/** return an interface with payload only if it presents */ +export type PayloadBasis< + Payload extends object = undefined +> = Payload extends undefined ? {} : { payload: Payload }; + +/** return an interface with meta only if it presents */ +export type MetaBasis = Meta extends undefined + ? {} + : { meta: Meta }; + +// ---------------------------------------- // diff --git a/definitions/index.d.ts b/definitions/index.d.ts new file mode 100644 index 0000000..5e9c8ce --- /dev/null +++ b/definitions/index.d.ts @@ -0,0 +1,19 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary Definitions for redux-logic + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +export * from './action'; +export * from './logic'; +export * from './middleware'; +export * from './utilities'; diff --git a/definitions/logic.d.ts b/definitions/logic.d.ts new file mode 100644 index 0000000..6f8fb54 --- /dev/null +++ b/definitions/logic.d.ts @@ -0,0 +1,312 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary Definitions for redux-logic + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +import { Subject } from 'rxjs'; + +import { Middleware } from 'redux'; + +import { + ArgumentAction, + Action, + ActionBasis, + Override, + StandardAction +} from './'; +import { Context } from 'vm'; + +// +// LOGIC +// + +/* * * * * * + | State is the type of the state stored in redux | + | Payload is the type of the payload of the handled action | + | Meta is the type of the meta object of the handled action | + | Dependency is the type of depObj excluding getState/action | + | Context is the type of the ctx object | + | Type is the type of the handled action | + * * * * * */ + +export type Logic< + State extends object = {}, + Payload extends object = undefined, + Meta extends object = undefined, + Dependency extends object = {}, + Context extends object = undefined, + Type extends string = string +> = Override< + CreateLogic.Config< + State, + Action, + Dependency, + Context, + Type + >, + { + name: string; + type: string; + cancelType: string; + } +>; + +/* ----- createLogic ----- */ + +export declare const createLogic: CreateLogic; + +export interface CreateLogic { + // full createLogic declaration + < + State extends object, + Payload extends object = undefined, + Meta extends object = undefined, + Dependency extends object = {}, + Context extends object = undefined, + Type extends string = string + >( + config: CreateLogic.Config< + State, + Action, + Dependency, + Context, + Type + > + ): Logic; + + // createLogic wihout context + < + State extends object, + Payload extends object = undefined, + Meta extends object = undefined, + Dependency extends object = {}, + Type extends string = string + >( + config: CreateLogic.Config< + State, + Action, + Dependency, + undefined, + Type + > + ): Logic; + + // createLogic wihout payload and meta + < + State extends object, + Dependency extends object = {}, + Context extends object = undefined, + Type extends string = string + >( + config: CreateLogic.Config, Dependency, Context, Type> + ): Logic; + + // createLogic with State and Type only + ( + config: CreateLogic.Config, {}, undefined, Type> + ): Logic; + + // createLogic with State, Dependency and Type only + < + State extends object, + Dependency extends object = {}, + Type extends string = string + >( + config: CreateLogic.Config, Dependency, undefined, Type> + ): Logic; +} + +export namespace CreateLogic { + export type Config< + State extends object, + Action extends StandardAction, + Dependency extends object, + Context extends object, + Type extends string + > = Config.Base & + ( + | Config.Validate + | Config.Transform) & + (Config.Process); + + export namespace Config { + /* ----- common ----- */ + + export type DepObj = Dependency & { + getState(): State; + action: Action; + }; + + export type PrimitiveType = + | Type + | RegExp + | Function; + + export type TypeMatcher< + Type extends string | symbol, + Payload extends object + > = PrimitiveType | PrimitiveType[]; + + export type Pass = ( + action: ArgumentAction & + (Context extends undefined + ? {} + : (Context extends undefined ? { ctx?: Context } : { ctx: Context })), + options?: { + useDispatch: boolean | 'auto'; + } + ) => void; + + export interface Base< + State extends object, + Action extends StandardAction, + Type extends string + > { + name?: string | Function; + type: TypeMatcher>; + cancelType?: TypeMatcher>; + latest?: boolean; + debounce?: number; + throttle?: number; + warnTimeout?: number; + } + + // ---------------------------------------- // + + /* ----- validate ----- */ + + interface Validate< + State, + Action extends ActionBasis, + Dependency extends object, + Context extends object + > { + validate?: Validate.Hook; + } + + export namespace Validate { + export type Hook< + State, + Action extends ActionBasis, + Dependency extends object, + Context extends object = undefined + > = ( + depObj: DepObj, + allow: Pass, + reject: Pass + ) => void; + } + + // ---------------------------------------- // + + /* ----- transform ----- */ + + interface Transform< + State, + Action extends ActionBasis, + Dependency extends object, + Context extends object + > { + transform?: Transform.Hook; + } + + export namespace Transform { + export type Hook< + State, + Action extends ActionBasis, + Dependency extends object, + Context extends object = undefined + > = ( + depObj: DepObj, + next: Pass + ) => void; + } + + // ---------------------------------------- // + + /* ----- process ----- */ + + type ActionCreator< + InputPayload extends object + > = InputPayload extends undefined + ? (payload?: InputPayload) => StandardAction + : (InputPayload extends Error + ? (error?: Error) => Action + : (payload?: InputPayload) => Action); + + type PayloadExtractor< + Action extends StandardAction + > = Action extends StandardAction + ? Payload + : undefined; + + export interface Process< + State extends object, + Action extends StandardAction, + Dependency extends object, + Context extends object = undefined + > { + processOptions?: Process.Options; + process?: + | Process.SimpleHook + | Process.AdvancedHook; + } + + export namespace Process { + export interface Options { + dispatchReturn?: boolean; + dispatchMultiple?: boolean; + successType?: string | ActionCreator>; + failType?: string | ActionCreator; + } + + export type DepObj< + State extends object, + Action extends StandardAction, + Dependency extends object, + Context extends object = undefined + > = Config.DepObj & { + cancelled$: Subject; + ctx: Context; + }; + + export type SimpleHook< + State extends object, + Action extends StandardAction, + Dependency extends object, + Context extends object = undefined + > = (depObj: Process.DepObj) => void; + + export type AdvancedHook< + State extends object, + Action extends StandardAction, + Dependency extends object, + Context extends object = undefined + > = (( + depObj: Process.DepObj, + dispatch: (action: ArgumentAction) => void, + done: () => void + ) => void); + } + + // ---------------------------------------- // + } +} + +// ---------------------------------------- // + +/* ----- configureLogic ----- */ + +export function configureLogic(options: { warnTimeout?: number }): void; + +// ---------------------------------------- // diff --git a/definitions/middleware.d.ts b/definitions/middleware.d.ts new file mode 100644 index 0000000..b5d406f --- /dev/null +++ b/definitions/middleware.d.ts @@ -0,0 +1,94 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary Definitions for redux-logic + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +import { Subject } from 'rxjs'; + +import { Middleware } from 'redux'; + +import { Action, ArgumentAction, Logic } from './'; + +// +// MIDDLEWARE +// + +interface LogicMiddleware< + State extends object = {}, + Dependency extends object = {}, + Context extends object = undefined, + Type extends string = string +> extends Middleware { + (store: CreateLogicMiddleware.Store): CreateLogicMiddleware.Next; + + monitor$: Subject<{ + action?: + | 'op' + | 'top' + | 'bottom' + | 'begin' + | 'nesxPartial; + + addDeps(additionalDeps: Partial): void; + + addLogic( + newLogics: Logic[] + ): { logicCount: number }; + + mergeNewLogic( + newLogics: Logic[] + ): { logicCount: number }; + + replaceLogic( + logics: Logic[] + ): { logicCount: number }; + + whenComplete(callback?: Fn): Promise; +} + +/* ----- createLogicMiddleware ----- */ + +export function createLogicMiddleware< + State extends object = {}, + Dependency extends object = {}, + Context extends object = undefined, + Type extends string = string +>( + logics?: Logic[], + deps?: Dependency +): LogicMiddleware; + +export namespace CreateLogicMiddleware { + export interface Store { + dispatch?: (action?: Action) => Action; + getState?: () => State; + } + + export type Next = (next: Function) => ActionCreator; + + export type ActionCreator = (action?: ArgumentAction) => Action; +} + +// ---------------------------------------- // diff --git a/definitions/utilities.d.ts b/definitions/utilities.d.ts new file mode 100644 index 0000000..216da3f --- /dev/null +++ b/definitions/utilities.d.ts @@ -0,0 +1,23 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary Definitions for redux-logic + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +// +// UTILITIES +// + +/** a simple helper for overring properties */ +export type Override = { + [K in keyof A]: K extends keyof B ? B[K] : A[K] +}; diff --git a/package-lock.json b/package-lock.json index 158cbd7..ade6d93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -183,6 +183,18 @@ } } }, + "@types/mocha": { + "version": "5.2.4", + "resolved": "https://npm.complex.network/@types%2fmocha/-/mocha-5.2.4.tgz", + "integrity": "sha512-XMHApnKWI0jvXU5gLcSTsRjJBpSzP0BG+2oGv98JFyS4a5R0tRy0oshHBRndb3BuHb9AwDKaUL8Ja7GfUvsG4g==", + "dev": true + }, + "@types/node": { + "version": "10.5.2", + "resolved": "https://npm.complex.network/@types%2fnode/-/node-10.5.2.tgz", + "integrity": "sha512-m9zXmifkZsMHZBOyxZWilMwmTlpC8x5Ty360JKTiXvlXZfBWYpsg9ZZvP/Ye+iZUh+Q+MxDLjItVTWIsfwz+8Q==", + "dev": true + }, "acorn": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", @@ -6100,6 +6112,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "2.8.4", + "resolved": "https://npm.complex.network/typescript/-/typescript-2.8.4.tgz", + "integrity": "sha512-IIU5cN1mR5J3z9jjdESJbnxikTrEz3lzAw/D0Tf45jHpBp55nY31UkUvmVHoffCfKHTqJs3fCLPDxknQTTFegQ==", + "dev": true + }, "uglify-js": { "version": "2.7.5", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", diff --git a/package.json b/package.json index c797d7c..1aed823 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "test:cov-codacy": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text mocha test --recursive && cat coverage/lcov.info | codacy-coverage", "test:rxbuild": "cross-env BABEL_ENV=commonjs mocha --compilers js:babel-register test/rxbuild.spec.js -r ./test/setup.js", "test:examples": "cross-env BABEL_ENV=commonjs babel-node examples/testAll.js", - "check:src": "npm run lint && npm run test && npm run test:rxbuild && npm run test:prod", + "test:typescript": "tsc --noEmit", + "check:src": "npm run lint && npm run test && npm run test:rxbuild && npm run test:prod && npm run test:typescript", "check:examples": "npm run build:examples && npm run test:examples", "build:commonjs": "cross-env BABEL_ENV=commonjs babel src --out-dir build-lib", "build:es": "cross-env BABEL_ENV=es babel src --out-dir build-es", @@ -80,6 +81,8 @@ "redux": "^3.5.2" }, "devDependencies": { + "@types/mocha": "^5.2.2", + "@types/node": "^10.3.4", "babel-cli": "^6.3.15", "babel-core": "^6.3.15", "babel-eslint": "^7.0.0", @@ -121,6 +124,7 @@ "nyc": "^13.0.0", "redux": "^3.5.2", "rimraf": "^2.3.4", + "typescript": "~2.8.0", "webpack": "^1.9.6" }, "npmName": "redux-logic", @@ -145,5 +149,6 @@ ], "sourceMap": false, "instrument": false - } + }, + "typings": "definitions/index.d.ts" } diff --git a/test/typecheck.action.ts b/test/typecheck.action.ts new file mode 100644 index 0000000..eced0dc --- /dev/null +++ b/test/typecheck.action.ts @@ -0,0 +1,174 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary A test for the typescript definition + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +import { Action, ErroneousAction, StandardAction } from '../definitions'; + +// +// Action Type +// + +const TEST_TYPE = 'type'; +type TestType = typeof TEST_TYPE; + +interface TestPayload { + data: 'payload'; +} + +interface TestMeta { + data: 'meta'; +} + +{ + const action: StandardAction = { + type: TEST_TYPE + }; +} + +{ + const action: ErroneousAction = { + type: TEST_TYPE, + payload: new Error('message'), + error: true + }; +} + +/* ----- Action ----- */ + +// The Action type should cover all types above. + +{ + const action: Action = { + type: TEST_TYPE + }; + + { + // would fail if test is 'type' instread of string + const test: string = action.type; + } + + { + // would fail if test is in ErroneousAction type + const test: StandardAction = action; + } +} + +{ + const action: Action = { + type: TEST_TYPE + }; + { + // now action.type can be in the type of 'type' + const test: TestType = action.type; + } + + { + // would fail if test is in ErroneousAction type + const test: StandardAction = action; + } +} + +{ + const action: Action = { + type: TEST_TYPE + }; + { + const test: TestType = action.type; + } + + { + // should pass with any the type for Payload and Meta + const test: StandardAction = action; + } +} + +{ + const action: Action = { + type: TEST_TYPE, + payload: { + data: 'payload' + }, + meta: { + data: 'meta' + } + }; + { + const test: TestType = action.type; + } + + { + // should pass with any the type for Payload and Meta + const test: StandardAction = action; + } +} + +{ + const action: Action = { + type: TEST_TYPE, + payload: { + data: 'payload' + }, + meta: { + data: 'meta' + } + }; + + // would fail if test is in ErroneousAction type + const test: StandardAction = action; +} + +{ + const action: Action = { + type: TEST_TYPE, + payload: new Error('message'), + error: true + }; + + // would fail if test is in StandardAction type + const test: ErroneousAction = action; +} + +{ + let action: Action; + + { + const test: boolean = action.error; + } + + if (action.error === true) { + // would fail if test is in StandardAction type + const test: ErroneousAction = action; + } else { + // would fail if test is in ErroneousAction type + const test: StandardAction = action; + } +} + +{ + let action: Action; + + { + const test: boolean = action.error; + } + + if (action.error === true) { + // would fail if test is in StandardAction type + const test: ErroneousAction = action; + } else { + // would fail if test is in ErroneousAction type + const test: StandardAction = action; + } +} + +// ---------------------------------------- // diff --git a/test/typecheck.createLogic.ts b/test/typecheck.createLogic.ts new file mode 100644 index 0000000..dd0233a --- /dev/null +++ b/test/typecheck.createLogic.ts @@ -0,0 +1,302 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary A test for the typescript definition + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +import { configureLogic, createLogic } from '../'; + +import { ErroneousAction, StandardAction } from '../'; +import { ActionBasis, MetaBasis, PayloadBasis } from '../'; + +import { Dependency, Meta, Payload, State } from './typecheck'; + +/* ----- configureLogic ----- */ + +{ + configureLogic({ + warnTimeout: 10000 + }); +} + +// ---------------------------------------- // + +/* ----- createLogic ----- */ + +{ + const fn = () => {}; + fn.toString = () => 'fn'; + const logic = createLogic({ + name: fn, + type: 'type' + }); +} + +{ + const fn1 = () => {}; + fn1.toString = () => 'type1'; + const fn2 = () => {}; + fn2.toString = () => 'type2'; + const logic = createLogic({ + type: [fn1, fn2] + }); +} + +{ + const logic = createLogic({ + type: 'type' + }); +} + +{ + const logic = createLogic({ + type: /type/ + }); +} + +{ + const logic = createLogic({ + type: ['type'] + }); +} + +{ + const logic = createLogic({ + type: [/type/] + }); +} + +{ + const logic = createLogic({ + type: 'type', + cancelType: 'type' + }); +} + +{ + const logic = createLogic({ + type: 'type', + cancelType: /type/ + }); +} + +{ + const logic = createLogic({ + type: 'type', + cancelType: ['type'] + }); +} + +{ + const logic = createLogic({ + type: 'type', + cancelType: [/type/] + }); +} + +{ + const logic = createLogic({ + type: 'type', + cancelType: () => ({ type: 'cancelType' }) + }); +} + +{ + const logic = createLogic({ + type: 'type', + cancelType: (): StandardAction<'cancelType', { source: string }> => ({ + type: 'cancelType', + payload: { source: 'somewhere' } + }) + }); +} + +{ + const logic = createLogic({ + type: 'type', + cancelType: [ + () => ({ type: 'cancelType1', payload: { source: 'somewhere' } }), + () => ({ type: 'cancelType2', payload: { source: 'somewhere' } }) + ] + }); +} + +{ + const logic = createLogic({ + name: 'name', + type: 'type', + debounce: 0, + throttle: 0, + latest: true, + warnTimeout: 60000 + }); +} + +{ + const logic = createLogic({ + type: 'type', + validate({ getState, action }, allow, reject) { + let state: State = getState(); + let expectedAction: + | (StandardAction<'type'>) + | ErroneousAction<'type'> = action; + allow(action); + reject(action); + } + }); +} + +{ + const logic = createLogic({ + type: 'type', + transform({ getState, action }, next) { + let state: State = getState(); + let expectedAction: + | (StandardAction<'type'>) + | ErroneousAction<'type'> = action; + next(action); + } + }); +} + +{ + const logic = createLogic({ + type: 'type', + process() {} + }); +} + +{ + const logic = createLogic({ + type: 'type', + processOptions: { + dispatchReturn: true, + successType: 'successType' + }, + process({ getState, action, cancelled$ }): void { + let state: State = getState(); + let expectedAction: + | (StandardAction<'type'>) + | ErroneousAction<'type'> = action; + + cancelled$.subscribe({ + next: () => {} + }); + } + }); +} + +{ + const logic = createLogic({ + type: 'type', + processOptions: { + dispatchReturn: true, + successType: (payload: Payload) => ({ type: 'successType' }) + }, + process({ getState, action, cancelled$ }) { + let state: State = getState(); + let expectedAction: + | (ActionBasis<'type'> & PayloadBasis & MetaBasis) + | ErroneousAction<'type'> = action; + } + }); +} + +{ + const logic = createLogic({ + type: 'type', + processOptions: { + dispatchReturn: true, + dispatchMultiple: false + }, + process(depObj) { + let dep: Dependency = depObj; + let state: State = depObj.getState(); + let expectedAction: (ActionBasis<'type'>) | ErroneousAction<'type'> = + depObj.action; + } + }); +} + +{ + const logic = createLogic({ + type: 'type', + processOptions: { + dispatchReturn: false, + dispatchMultiple: true + }, + process(depObj, dispatch, done) { + let state: State = depObj.getState(); + let expectedAction: (ActionBasis<'type'>) | ErroneousAction<'type'> = + depObj.action; + + dispatch({ + type: 'newType' + }); + done(); + } + }); +} + +{ + const logic = createLogic({ + type: 'type', + debounce: 40, + process({ action }, dispatch) { + setTimeout(() => { + dispatch({ + ...action, + type: 'newType' + }); + }, 100); + } + }); +} + +{ + let meta: Meta; + + const logic = createLogic({ + type: 'type', + debounce: 40, + process({ action }, dispatch) { + setTimeout(() => { + dispatch({ + ...action, + type: 'newType', + meta + }); + }, 100); + } + }); +} + +// +// EXPECT ERROR +// + +// { +// const logic = createLogic(); +// } + +// { +// const logic = createLogic({}); +// } + +// { +// const logic = createLogic({ +// type: '*', +// processOptions: { +// warnTimeout: 120000 +// } +// }); +// } diff --git a/test/typecheck.middleware.ts b/test/typecheck.middleware.ts new file mode 100644 index 0000000..9c26cc4 --- /dev/null +++ b/test/typecheck.middleware.ts @@ -0,0 +1,133 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary A test for the typescript definition + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +import { Observable, Subject } from 'rxjs'; + +import { createLogicMiddleware } from '../'; + +import { Action, ArgumentAction, Logic } from '../'; +import { Payload, Meta } from './typecheck'; + +// +// createLogicMiddleware +// + +interface Dependency { + depKey: string; + depKey2?: string; +} + +let dependency: Dependency; + +let logicArray: Logic[]; + +{ + const middleware = createLogicMiddleware(); + middleware({}); +} + +{ + const middleware = createLogicMiddleware(logicArray, dependency); + + { + middleware.whenComplete(() => {}); + middleware.whenComplete().then(() => {}); + middleware.whenComplete(() => {}).then(() => {}); + } + + { + let next: (action?: ArgumentAction) => Action; + + const monArr = []; + let monitor: Subject<{ + action?: + | 'op' + | 'top' + | 'bottom' + | 'begin' + | 'nesxPartial = + middleware.monitor$; + monitor.subscribe(x => monArr.push(x)); + + const action = { type: 'type' }; + middleware({})(next)(action); + } + + { + const additionalDeps = { + depKey2: 'string' + }; + + middleware.addDeps(additionalDeps); + } + + middleware.addLogic(logicArray); + middleware.mergeNewLogic(logicArray); + middleware.replaceLogic(logicArray); +} + +{ + let dispatch: (action: Action) => Action; + const next = () => {}; + + const middleware = createLogicMiddleware(logicArray); + const storeFn = middleware({ dispatch })(next); + + { + let simpleAction: Action<'type'>; + let fullAction: Action<'type', Payload, Meta>; + + storeFn(simpleAction); + storeFn(fullAction); + } + + { + Observable.merge( + // fast 0, 1, 2 + Observable.interval(10) + .take(3) + .map(x => ({ meta: { fast: x } })), + Observable.interval(60) + .take(4) + .delay(40) + .map(x => ({ meta: { slow: x } })) + ).subscribe(x => { + storeFn({ + ...x, + type: 'type' + }); + }); + } +} + +// +// EXPECT ERROR +// + +// { +// const middleware = createLogicMiddleware({}); +// } diff --git a/test/typecheck.ts b/test/typecheck.ts new file mode 100644 index 0000000..8908160 --- /dev/null +++ b/test/typecheck.ts @@ -0,0 +1,31 @@ +/* + * *** MIT LICENSE *** + * ------------------------------------------------------------------------- + * This code may be modified and distributed under the MIT license. + * See the LICENSE file for details. + * ------------------------------------------------------------------------- + * + * @summary Shared types for typechecking + * + * @author Alvis HT Tang + * @license MIT + * @copyright Copyright (c) 2018 - All Rights Reserved. + * ------------------------------------------------------------------------- + */ + +export interface Dependency { + depKey: string; + optKey?: string; +} + +export interface Meta { + metaKey: string; +} + +export interface Payload { + payloadKey: number; +} + +export interface State { + stateKey: string; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0ed8f0e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "noImplicitAny": false, + "noImplicitUseStrict": false, + "noLib": false, + "noResolve": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "lib": [ + "dom", + "es2017" + ], + "typeRoots": [ + "node_modules/@types" + ] + }, + "include": [ + "**/*.ts", + "**/*.d.ts" + ], + "exclude": [ + "dist", + "node_modules" + ] +}