Skip to content

Commit a41fb2e

Browse files
committed
Check-in
0 parents  commit a41fb2e

10 files changed

+410
-0
lines changed

.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["es2015"]
3+
}

.gitignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# IntelliJ IDEA
2+
.idea
3+
*.iml
4+
5+
# NPM
6+
node_modules
7+
npm-*.log
8+
9+
# OS X
10+
.DS_Store

LICENSE.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 Daniel Lytkin
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Redux TypeScript Actions
2+
3+
A simple Action Creator library for TypeScript. Its goal is to provide simple
4+
yet type-safe experience with Redux actions.
5+
Created actions are FSA-compliant:
6+
7+
```ts
8+
interface Action<P> {
9+
type: string;
10+
payload?: P;
11+
error?: boolean;
12+
meta?: Object;
13+
}
14+
```
15+
16+
## Usage
17+
18+
### Basic
19+
20+
```ts
21+
import actionCreatorFactory from 'redux-typescript-actions';
22+
23+
const actionCreator = actionCreatorFactory();
24+
25+
// Specify payload shape as generic type argument.
26+
const somethingHappened = actionCreator<{foo: string}>('SOMETHING_HAPPENED');
27+
28+
// Get action creator type.
29+
console.log(somethingHappened.type); // SOMETHING_HAPPENED
30+
31+
// Create action.
32+
const action = somethingHappened({foo: 'bar'});
33+
console.log(action); // {type: 'SOMETHING_HAPPENED', payload: {foo: 'bar'}}
34+
```
35+
36+
### Async Action Creators
37+
38+
Async Action Creators are objects with properties `started`, `done` and
39+
`failed` whose values are action creators.
40+
41+
```ts
42+
import actionCreatorFactory from 'redux-typescript-actions';
43+
44+
const actionCreator = actionCreatorFactory();
45+
46+
// specify parameters and result shapes as generic type arguments
47+
const doSomething =
48+
actionCreator.async<{foo: string}, {bar: number}>('DO_SOMETHING');
49+
50+
console.log(doSomething.started({foo: 'lol'}));
51+
// {type: 'DO_SOMETHING_STARTED', payload: {foo: 'lol'}}
52+
53+
console.log(doSomething.done({
54+
params: {foo: 'lol'},
55+
result: {bar: 42},
56+
});
57+
// {type: 'DO_SOMETHING_DONE', payload: {
58+
// params: {foo: 'lol'},
59+
// result: {bar: 42},
60+
// }}
61+
62+
console.log(doSomething.failed({
63+
params: {foo: 'lol'},
64+
error: {code: 42},
65+
});
66+
// {type: 'DO_SOMETHING_FAILED', payload: {
67+
// params: {foo: 'lol'},
68+
// error: {code: 42},
69+
// }, error: true}
70+
```
71+
72+
### Actions With Type Prefix
73+
74+
You can specify a prefix that will be prepended to all action types. This is
75+
useful to namespace library actions as well as for large projects where it's
76+
convenient to keep actions near the component that dispatches them.
77+
78+
```ts
79+
// MyComponent.actions.ts
80+
import actionCreatorFactory from 'redux-typescript-actions';
81+
82+
const actionCreator = actionCreatorFactory('MyComponent');
83+
84+
const somethingHappened = actionCreator<{foo: string}>('SOMETHING_HAPPENED');
85+
86+
const action = somethingHappened({foo: 'bar'});
87+
console.log(action);
88+
// {type: 'MyComponent/SOMETHING_HAPPENED', payload: {foo: 'bar'}}
89+
```
90+
91+
### Reducers
92+
93+
```ts
94+
// actions.ts
95+
import actionCreatorFactory from 'redux-typescript-actions';
96+
97+
const actionCreator = actionCreatorFactory();
98+
99+
export const somethingHappened =
100+
actionCreator<{foo: string}>('SOMETHING_HAPPENED');
101+
102+
103+
// reducer.ts
104+
import {Action} from 'redux';
105+
import {isType, Action} from 'redux-typescript-actions';
106+
import {somethingHappened} from './actions';
107+
108+
type State = {bar: string};
109+
110+
const reducer = (state: State, action: Action): State => {
111+
if (isType(action, somethingHappened)) {
112+
// action.payload is inferred as {foo: string};
113+
114+
action.payload.bar; // error
115+
116+
return {bar: action.payload.foo};
117+
}
118+
119+
return state;
120+
};
121+
```
122+
123+
## API
124+
125+
### `actionCreatorFactory(prefix?: string): ActionCreatorFactory`
126+
127+
Creates Action Creator factory with optional prefix for action types.
128+
129+
* `prefix?: string`: Prefix to be prepended to action types.
130+
131+
### `isType(action: Action, actionCreator: ActionCreator): boolean`
132+
133+
Returns `true` if action has the same type as action creator. Defines
134+
[Type Guard](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
135+
that lets TypeScript know `payload` type inside blocks where `isType` returned
136+
`true`:
137+
138+
```ts
139+
const somethingHappened = actionCreator<{foo: string}>('SOMETHING_HAPPENED');
140+
141+
if (isType(action, somethingHappened)) {
142+
// action.payload has type {foo: string};
143+
}
144+
```

es6/index.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export function isType(action, actionCreator) {
2+
return action.type === actionCreator.type;
3+
}
4+
export default function actionCreatorFactory(prefix) {
5+
const actionTypes = {};
6+
function actionCreator(type, commonMeta, error) {
7+
if (actionTypes[type])
8+
throw new Error(`Duplicate action type: ${type}`);
9+
actionTypes[type] = true;
10+
const fullType = prefix ? `${prefix}/${type}` : type;
11+
return Object.assign((payload, meta) => {
12+
const action = {
13+
type: fullType,
14+
payload,
15+
meta: Object.assign({}, commonMeta, meta),
16+
};
17+
if (error)
18+
action.error = error;
19+
return action;
20+
}, { type: fullType });
21+
}
22+
function asyncActionCreators(type, commonMeta) {
23+
return {
24+
type: prefix ? `${prefix}/${type}` : type,
25+
started: actionCreator(`${type}_STARTED`, commonMeta),
26+
done: actionCreator(`${type}_DONE`, commonMeta),
27+
failed: actionCreator(`${type}_FAILED`, commonMeta, true),
28+
};
29+
}
30+
return Object.assign(actionCreator, { async: asyncActionCreators });
31+
}

index.d.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Action as ReduxAction } from "redux";
2+
export interface Action<P> extends ReduxAction {
3+
type: string;
4+
payload?: P;
5+
error?: boolean;
6+
meta?: Object;
7+
}
8+
export declare function isType<P>(action: ReduxAction, actionCreator: ActionCreator<P>): action is Action<P>;
9+
export interface ActionCreator<P> {
10+
type: string;
11+
(payload?: P, meta?: Object): Action<P>;
12+
}
13+
export interface AsyncActionCreators<P, R> {
14+
type: string;
15+
started: ActionCreator<P>;
16+
done: ActionCreator<{
17+
params: P;
18+
result: R;
19+
}>;
20+
failed: ActionCreator<{
21+
params: P;
22+
error: any;
23+
}>;
24+
}
25+
export interface ActionCreatorFactory {
26+
<P>(type: string, commonMeta?: Object, error?: boolean): ActionCreator<P>;
27+
async<P, S>(type: string, commonMeta?: Object): AsyncActionCreators<P, S>;
28+
}
29+
export default function actionCreatorFactory(prefix?: string): ActionCreatorFactory;

lib/index.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use strict";
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.isType = isType;
7+
exports.default = actionCreatorFactory;
8+
function isType(action, actionCreator) {
9+
return action.type === actionCreator.type;
10+
}
11+
function actionCreatorFactory(prefix) {
12+
var actionTypes = {};
13+
function actionCreator(type, commonMeta, error) {
14+
if (actionTypes[type]) throw new Error("Duplicate action type: " + type);
15+
actionTypes[type] = true;
16+
var fullType = prefix ? prefix + "/" + type : type;
17+
return Object.assign(function (payload, meta) {
18+
var action = {
19+
type: fullType,
20+
payload: payload,
21+
meta: Object.assign({}, commonMeta, meta)
22+
};
23+
if (error) action.error = error;
24+
return action;
25+
}, { type: fullType });
26+
}
27+
function asyncActionCreators(type, commonMeta) {
28+
return {
29+
type: prefix ? prefix + "/" + type : type,
30+
started: actionCreator(type + "_STARTED", commonMeta),
31+
done: actionCreator(type + "_DONE", commonMeta),
32+
failed: actionCreator(type + "_FAILED", commonMeta, true)
33+
};
34+
}
35+
return Object.assign(actionCreator, { async: asyncActionCreators });
36+
}

package.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "redux-typescript-actions",
3+
"version": "1.0.0",
4+
"description": "Type-safe action creator utilities",
5+
"keywords": [
6+
"redux"
7+
],
8+
"main": "lib/index.js",
9+
"jsnext:main": "es6/index.js",
10+
"typings": "./index.d.ts",
11+
"files": [
12+
"es6",
13+
"lib",
14+
"index.d.ts"
15+
],
16+
"scripts": {
17+
"build:es6": "tsc",
18+
"build:commonjs": "babel es6 --out-dir lib",
19+
"build": "npm run build:es6 && npm run build:commonjs"
20+
},
21+
"author": "Daniel Lytkin <[email protected]>",
22+
"license": "MIT",
23+
"dependencies": {
24+
"redux": "^3.6.0"
25+
},
26+
"devDependencies": {
27+
"babel-core": "^6.17.0",
28+
"babel-preset-es2015": "^6.16.0",
29+
"typescript": "^2.0.3"
30+
}
31+
}

0 commit comments

Comments
 (0)