Skip to content

Commit

Permalink
Merge pull request #52 from psachs21/master
Browse files Browse the repository at this point in the history
Provide typing support for actions without payloads.
  • Loading branch information
pauldijou authored Aug 9, 2017
2 parents 8dd9890 + 83498c4 commit 8a5252d
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 15 deletions.
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ You can also use a [browser friendly compiled file](https://unpkg.com/redux-act@
- [Adding and removing actions](#adding-and-removing-actions)
- [Async actions](#async-actions)
- [Enable or disable batch](#enable-or-disable-batch)
- [Typescript](#typescript)
- [Loggers](#loggers)
- [Redux Logger](#redux-logger)

Expand Down Expand Up @@ -748,6 +749,53 @@ reducer.options({ payload: false });
reducer.on(batch, (state, action) => action.payload.reduce(reducer, state));
```

### Typescript

We've built some basic typings around this API that will help Typescript identify potential issues in your code.

You can use any of the existing methods to create reducers and Typescript will work (as a superset of Javascript) but that kind of defeats some of the benefits
of Typescript. For this reason, the following is the recommended way to create a reducer.

```typescript
import { createReducer, createAction } from 'redux-act';

const defaultState = {
canCount: false,
canBounce: false,
count: 0
};

const setCanCount = createAction<boolean>('Set whether you can set count');

const reducer = createReducer<typeof defaultState>({}, defaultState);

reducer.on(setCanCount, (state, payload) => ({
...state,
canCount: payload
}));
```

Using the `reducer.on()` API, Typescript will identify the payload set on `setCanCount` and provide that type as payload. This can be really handy once
your code starts scaling up.

#### Caveats

Due to some limitations on the typescript typing system, our action creators have some limitations but you can create typed actions creators assuming you have no
payload reducer.

```typescript
import { createAction } from 'redux-act';

const action = createAction<boolean>('Some type');
const emptyAction = createAction('Another type');
const otherAction = createAction<boolean>('Other action', (arg1, arg2) => ({ arg1, arg2 }));
```

`action` and `emptyAction` will provide typing support, making sure `action` is provided a boolean as an arg, or `emptyAction` is not provided an argument at all.

`otherAction`, on the otherhand, will be able to be called with any arguments, regardless of what the payload reducer expects.


## Loggers

`redux-act` provides improved logging with some loggers, mostly for batched actions.
Expand Down
7 changes: 7 additions & 0 deletions test/typescript.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ const simpleAct = createAction<boolean>('something');
// Simple actions provide validation on params.
const action = simpleAct(true);

const emptyAction = createAction('label');

// Empty actions don't have payload.
emptyAction();

// Invalid: emptyAction(null);

function onOff(on, off) {
on(act1, () => 1)
}
Expand Down
33 changes: 18 additions & 15 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,40 @@ interface StoreWithDisbatch<S> extends Store<S> {

type StoreOrDispatch = Store<any> | Dispatch | Store<any>[] | Dispatch[]

// Action creators
interface ComplexActionCreator<P, M={}> {
(...args: any[]): Action<P, M>;
interface BaseActionCreator<T> {
getType(): string;

assignTo(arg: StoreOrDispatch): ComplexActionCreator<P, M>;
bindTo(arg: StoreOrDispatch): ComplexActionCreator<P, M>;

assigned(): boolean;
bound(): boolean;
dispatched(): boolean;

assignTo(arg: StoreOrDispatch): T;
bindTo(arg: StoreOrDispatch): T;
}

// Action creators
interface ComplexActionCreator<P, M={}> extends BaseActionCreator<ComplexActionCreator<P, M>> {
(...args: any[]): Action<P, M>;

raw(...args: any[]): Action<P, M>;
}

interface SimpleActionCreator<P, M={}> {
interface SimpleActionCreator<P, M={}> extends BaseActionCreator<SimpleActionCreator<P, M>> {
(payload: P): Action<P, M>;
getType(): string;

assignTo(arg: StoreOrDispatch): SimpleActionCreator<P, M>;
bindTo(arg: StoreOrDispatch): SimpleActionCreator<P, M>;
raw(payload: P): Action<P, M>;
}

assigned(): boolean;
bound(): boolean;
dispatched(): boolean;
interface EmptyActionCreator extends BaseActionCreator<EmptyActionCreator> {
(): Action<null, null>;

raw(payload: P): Action<P, M>;
raw(): Action<null, null>;
}

type ActionCreator<P, M={}> = SimpleActionCreator<P, M> | ComplexActionCreator<P, M>;
type ActionCreator<P, M={}> = SimpleActionCreator<P, M> | ComplexActionCreator<P, M> | EmptyActionCreator;

export function createAction(): EmptyActionCreator;
export function createAction(description: string): EmptyActionCreator;
export function createAction<P, M={}>(): SimpleActionCreator<P, M>;
export function createAction<P, M={}>(description: string): SimpleActionCreator<P, M>;
export function createAction<P, M={}>(description: string, payloadReducer: (...args: any[]) => P): ComplexActionCreator<P, M>;
Expand Down

0 comments on commit 8a5252d

Please sign in to comment.