diff --git a/src/index.ts b/src/index.ts index 39738c6..767c95a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,12 +4,16 @@ export interface AnyAction { export type Meta = null | {[key: string]: any}; -export interface Action extends AnyAction { +export type Action = Payload extends void ? { + type: string; + error?: boolean; + meta?: Meta; +} : { type: string; payload: Payload; error?: boolean; meta?: Meta; -} +}; /** * Returns `true` if action has the same type as action creator. @@ -67,17 +71,52 @@ export interface ActionCreator { * @param payload Action payload. * @param meta Action metadata. Merged with `commonMeta` of Action Creator. */ - (payload: Payload, meta?: Meta): Action; + (...a: (Payload extends void ? []: never)): Action; + (params: Payload, meta?: Meta): Action; } -export type Success = ( - | {params: Params} - | (Params extends void ? {params?: Params} : never)) & - ({result: Result} | (Result extends void ? {result?: Result} : never)); +type EmptyObject = { + [K in any] : never; +}; + +export type Success = + Params extends void + ? ( + Result extends void + ? EmptyObject + : { + result: Result; + } + ) + : ( + Result extends void ? { + params: Params; + } : never) + | { + params: Params; + result: Result; + } +; export type Failure = ( - | {params: Params} - | (Params extends void ? {params?: Params} : never)) & {error: Error}; + Params extends void + ? ( + Error extends void + ? EmptyObject + : { + error: Error; + } + ) + : ( + Error extends void + ? { + params: Params; + }: never) + | { + params: Params; + error: Error; + } +); export interface AsyncActionCreators { type: string; @@ -156,7 +195,7 @@ export function actionCreatorFactory( function actionCreator( type: string, commonMeta?: Meta, - isError: ((payload: Payload) => boolean) | boolean = defaultIsError, + isError: ((payload?: Payload) => boolean) | boolean = defaultIsError, ) { const fullType = base + type; @@ -168,11 +207,11 @@ export function actionCreatorFactory( } return Object.assign( - (payload: Payload, meta?: Meta) => { - const action: Action = { + (payload?: Payload, meta?: Meta) => { + const action = { type: fullType, payload, - }; + } as Action; if (commonMeta || meta) { action.meta = Object.assign({}, commonMeta, meta); diff --git a/tests/index.ts b/tests/index.ts index 4c1cc96..8f89d18 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -56,6 +56,8 @@ test('meta', assert => { const someAction = actionCreator('ACTION_TYPE'); + const actionWithoutPayload = someAction(); + const action = someAction(undefined, {foo: 'bar'}); assert.deepEqual(action.meta, {foo: 'bar'}); diff --git a/tests/typings/index.ts b/tests/typings/index.ts index 4531e3b..3e7a65a 100644 --- a/tests/typings/index.ts +++ b/tests/typings/index.ts @@ -1,4 +1,4 @@ -import actionCreatorFactory, {isType, AnyAction} from "typescript-fsa"; +import actionCreatorFactory, {isType, AnyAction, AsyncActionCreators, Success, Failure} from "typescript-fsa"; declare const action: AnyAction; @@ -9,6 +9,7 @@ const actionCreator = actionCreatorFactory(); function testPayload() { const withPayload = actionCreator<{foo: string}>('WITH_PAYLOAD'); const withoutPayload = actionCreator('WITHOUT_PAYLOAD'); + const withOrWithoutPayload = actionCreator('WITH_ORWITHOUT_PAYLOAD') // typings:expect-error const a = withPayload(); @@ -21,6 +22,11 @@ function testPayload() { const f = withoutPayload(undefined, {meta: 'meta'}); // typings:expect-error const g = withoutPayload({foo: 'bar'}); + + const h = withOrWithoutPayload('string'); + const i = withOrWithoutPayload(); + // typings:expect-error + const j = withOrWithoutPayload(111); } function testAsyncPayload() { @@ -171,33 +177,40 @@ function testAsyncNoParamsAndResult() { } function testAsyncGeneric(params: P, result: R) { - const async = actionCreator.async('ASYNC'); + const async = actionCreator.async('ASYNC'); const started = async.started(params); + // typings:expect-error const started1 = async.started({}); // typings:expect-error const started2 = async.started(); - const done = async.done({ - params, - result, - }); + if(params !== undefined && result !== undefined) { + const done = async.done({ + params, + result, + } as Success); + } + // typings:expect-error const done1 = async.done({ params: {foo: 1}, - result, + result: params[1], }); // typings:expect-error const done2 = async.done({ - params, + params: params[0], result: {bar: 1}, }); - const failed = async.failed({ - params, - error: {baz: 'baz'}, - }); + if(params !== undefined) { + const failed = async.failed({ + params, + error: {baz: 'baz'}, + } as Failure); + } + // typings:expect-error const failed1 = async.failed({ params: {foo: 1}, @@ -205,10 +218,15 @@ function testAsyncGeneric(params: P, result: R) { }); } +var voidValue = (function () { })(); + function testAsyncGenericStrictError(params: P, result: R, error: E) { const async = actionCreator.async('ASYNC'); - const started = async.started(params); + if(params === undefined) { + const started = (async as AsyncActionCreators as AsyncActionCreators).started(); + } + // typings:expect-error const started1 = async.started({}); // typings:expect-error @@ -217,7 +235,8 @@ function testAsyncGenericStrictError(params: P, result: R, error: E) { const done = async.done({ params, result, - }); + } as Success); + // typings:expect-error const done1 = async.done({ params: {foo: 1}, @@ -232,7 +251,8 @@ function testAsyncGenericStrictError(params: P, result: R, error: E) { const failed = async.failed({ params, error, - }); + } as Failure); + // typings:expect-error const failed1 = async.failed({ params: {foo: 1}, diff --git a/yarn.lock b/yarn.lock index 0a8940a..6c20149 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27,11 +27,10 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - dependencies: - color-convert "^1.9.0" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== arr-diff@^2.0.0: version "2.0.0" @@ -47,10 +46,6 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - babel-code-frame@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" @@ -92,24 +87,6 @@ chalk@^1.1.0: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.3.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -color-convert@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" - dependencies: - color-name "^1.1.1" - -color-name@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - colors@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" @@ -143,10 +120,15 @@ defined@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" -diff@^3.1.0, diff@^3.2.0: +diff@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + es-abstract@^1.5.0: version "1.7.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c" @@ -164,7 +146,7 @@ es-to-primitive@^1.1.1: is-date-object "^1.0.1" is-symbol "^1.0.1" -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -290,10 +272,6 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - has@^1.0.1, has@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" @@ -441,20 +419,14 @@ micromatch@^2.3.11: dependencies: brace-expansion "^1.0.0" -minimist@0.0.8, minimist@~0.0.1: +minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^1.2.0, minimist@~1.2.0: +minimist@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - normalize-path@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -598,9 +570,10 @@ semver@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" -source-map-support@^0.5.3: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13" +source-map-support@^0.5.6: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -631,12 +604,6 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" - dependencies: - has-flag "^3.0.0" - tape@^4.6.2: version "4.6.3" resolved "https://registry.yarnpkg.com/tape/-/tape-4.6.3.tgz#637e77581e9ab2ce17577e9bd4ce4f575806d8b6" @@ -659,18 +626,16 @@ through@~2.3.4, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" -ts-node@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-6.0.3.tgz#28bf74bcad134fad17f7469dad04638ece03f0f4" +ts-node@^8.4.0: + version "8.6.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" + integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== dependencies: - arrify "^1.0.0" - chalk "^2.3.0" - diff "^3.1.0" + arg "^4.1.0" + diff "^4.0.1" make-error "^1.1.1" - minimist "^1.2.0" - mkdirp "^0.5.1" - source-map-support "^0.5.3" - yn "^2.0.0" + source-map-support "^0.5.6" + yn "3.1.1" tslib@^1.6.0: version "1.7.0" @@ -725,6 +690,7 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -yn@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==