Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional action payload #82

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 52 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ export interface AnyAction {

export type Meta = null | {[key: string]: any};

export interface Action<Payload> extends AnyAction {
export type Action<Payload> = 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.
Expand Down Expand Up @@ -67,17 +71,52 @@ export interface ActionCreator<Payload> {
* @param payload Action payload.
* @param meta Action metadata. Merged with `commonMeta` of Action Creator.
*/
(payload: Payload, meta?: Meta): Action<Payload>;
(...a: (Payload extends void ? []: never)): Action<void>;
(params: Payload, meta?: Meta): Action<Payload>;
}

export type Success<Params, Result> = (
| {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, Result> =
Params extends void
? (
Result extends void
? EmptyObject
: {
result: Result;
}
)
: (
Result extends void ? {
params: Params;
} : never)
| {
params: Params;
result: Result;
}
;

export type Failure<Params, Error> = (
| {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<Params, Result, Error = {}> {
type: string;
Expand Down Expand Up @@ -156,7 +195,7 @@ export function actionCreatorFactory(
function actionCreator<Payload>(
type: string,
commonMeta?: Meta,
isError: ((payload: Payload) => boolean) | boolean = defaultIsError,
isError: ((payload?: Payload) => boolean) | boolean = defaultIsError,
) {
const fullType = base + type;

Expand All @@ -168,11 +207,11 @@ export function actionCreatorFactory(
}

return Object.assign(
(payload: Payload, meta?: Meta) => {
const action: Action<Payload> = {
(payload?: Payload, meta?: Meta) => {
const action = {
type: fullType,
payload,
};
} as Action<Payload>;

if (commonMeta || meta) {
action.meta = Object.assign({}, commonMeta, meta);
Expand Down
2 changes: 2 additions & 0 deletions tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'});
Expand Down
50 changes: 35 additions & 15 deletions tests/typings/index.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,6 +9,7 @@ const actionCreator = actionCreatorFactory();
function testPayload() {
const withPayload = actionCreator<{foo: string}>('WITH_PAYLOAD');
const withoutPayload = actionCreator('WITHOUT_PAYLOAD');
const withOrWithoutPayload = actionCreator<string | void>('WITH_ORWITHOUT_PAYLOAD')

// typings:expect-error
const a = withPayload();
Expand All @@ -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() {
Expand Down Expand Up @@ -171,44 +177,56 @@ function testAsyncNoParamsAndResult() {
}

function testAsyncGeneric<P, R>(params: P, result: R) {
const async = actionCreator.async<P, R>('ASYNC');
const async = actionCreator.async<P, R, any>('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<P, R>);
}

// 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<P, any>);
}

// typings:expect-error
const failed1 = async.failed({
params: {foo: 1},
error: {baz: 'baz'},
});
}

var voidValue = (function () { })();

function testAsyncGenericStrictError<P, R, E>(params: P, result: R, error: E) {
const async = actionCreator.async<P, R, E>('ASYNC');

const started = async.started(params);
if(params === undefined) {
const started = (async as AsyncActionCreators<unknown, R, E> as AsyncActionCreators<void, R, E>).started();
}

// typings:expect-error
const started1 = async.started({});
// typings:expect-error
Expand All @@ -217,7 +235,8 @@ function testAsyncGenericStrictError<P, R, E>(params: P, result: R, error: E) {
const done = async.done({
params,
result,
});
} as Success<P, R>);

// typings:expect-error
const done1 = async.done({
params: {foo: 1},
Expand All @@ -232,7 +251,8 @@ function testAsyncGenericStrictError<P, R, E>(params: P, result: R, error: E) {
const failed = async.failed({
params,
error,
});
} as Failure<P, E>);

// typings:expect-error
const failed1 = async.failed({
params: {foo: 1},
Expand Down
Loading