Package Exports
- ts-action
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (ts-action) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
ts-action
What is it?
ts-action is a package for declaring Redux action creators with less TypeScript cruft.
Why might you need it?
I wanted a mechanism for declaring and consuming actions that involved writing as little boilerplate as possible. If you, too, want less cruft, you might find this package useful.
Also, if you are using NgRx or redux-observable, you might find the ts-action-operators package useful, too.
For an in-depth look at TypeScript, Redux and ts-action, have a look at: How to Reduce Action Boilerplate.
Install
Install the package using npm:
npm install ts-action --save-devUsage
Action creators are declared using the action method:
import { action, empty } from "ts-action";
const Foo = action("FOO", empty());Actions are created using the action creator as a class:
store.dispatch(new Foo());For actions with payloads, the payload type is specified using the payload method:
import { action, payload } from "ts-action";
const Foo = action("FOO", payload<{ foo: number }>());and the payload value is specified when creating the action:
store.dispatch(new Foo({ foo: 42 }));To have the properties added to the action itself - rather than a payload property - use the props method instead.
Action creators have type and action properties that can be used to narrow an action's TypeScript type in a reducer.
The action types can be combined into a discriminated union and the action can be narrowed to a specific TypeScript type using a switch statement, like this:
import { action, payload } from "ts-action";
const Foo = action("FOO", payload<{ foo: number }>());
const Bar = action("BAR", payload<{ bar: number }>());
const All = union(Foo, Bar);
type State = { foo?: number, bar?: number };
function fooBarReducer(state: State = {}, action: typeof All): State {
switch (action.type) {
case Foo.type:
return { ...state, foo: action.payload.foo };
case Bar.type:
return { ...state, bar: action.payload.bar };
default:
return state;
}
}Or, the package's isType method can be used to narrow the action's type using if statements, like this:
import { action, isType, payload } from "ts-action";
const Foo = action("FOO", payload<{ foo: number }>());
const Bar = action("BAR", payload<{ bar: number }>());
function fooBarReducer(state: State = {}, action: Action): State {
if (isType(action, Foo)) {
return { ...state, foo: action.payload.foo };
}
if (isType(action, Bar)) {
return { ...state, bar: action.payload.bar };
}
return state;
}API
action
The action method returns an action creator:
const Foo = action("FOO", empty());Action creators are classes and actions are be created using new:
store.dispatch(new Foo());The type option passed to the action method will be assigned to the created action's type property. The value passed should be either a literal or a literal type. That is, this is fine:
const Foo = action("FOO", empty());And this is fine, too:
const FOO = "FOO"; // Equivalent to: const FOO: "FOO" = "FOO";
const Foo = action(FOO, empty());However, with the following, TypeScript will be unable to narrow the action in a discriminated union:
let foo: string = "FOO";
const Foo = action(foo, empty());And the type option passed to the action method can be obtained using the creator's static type property:
switch (action.type) {
case Foo.type:
return { ...state, foo: action.payload.foo };
default:
return state;
}empty
empty is a method that's used to construct the options passed to the action method. To declare an action without a payload or properties , call it like this:
action("FOO", empty());Note that the spread syntax is used, as payload merges more that one option.
payload
payload is a method that's used to construct the options passed to the action method. To declare a payload, call it like this, specifying the type:
action("FOO", payload<number>());Note that the spread syntax is used, as payload merges more that one option.
props
props is a method that's used to construct the options passed to the action method. To declare properties, call it like this, specifying the type:
action("FOO", props<{ name: string }>());Note that the spread syntax is used, as props merges more that one option.
The props method is similar to the payload method, but with props, the specified properties are added to the action itself - rather than a payload property.
base
base is a method that's used to construct the options passed to the action method. To declare a base class with properties, call it like this:
action("FOO", base(class { constructor(public foo: number) {} }));The base method is similar to the props method, but with offers more control over property defaults, etc. as the base class is declared inline.
union
The union method can be used to infer a union of actions - for type narrowing using a discriminated union. It's passed two or more action creators and returns a value that can be used with TypeScript's typeof operator, like this:
const All = union(Foo, Bar);
function reducer(state: any = [], action: typeof All): any {
switch (action.type) {
case Foo.type:
return ... // Here the action will be narrowed to Foo.
case Bar.type:
return ... // Here the action will be narrowed to Bar.
default:
return state;
}
}isType
isType is a TypeScript type guard that will return either true or false depending upon whether the passed action is of the appropriate type. TypeScript will narrow the type when used with an if statement.
For example:
if (isType(action, Foo)) {
// Here, TypeScript has narrowed the type, so the action is strongly typed
// and individual properties can be accessed in a type-safe manner.
}guard
guard is a higher-order equivalent of isType. That is, it returns a TypeScript type guard that will, in turn, return either true or false depending upon whether the passed action is of the appropriate type. The guard method is useful when dealing with APIs that accept type guards.
For example, Array.prototype.filter accepts a type guard:
const actions = [new Foo(), new Bar()];
const filtered = actions.filter(guard(Foo)); // Inferred to be: const filtered: Foo[]