Skip to content

New API - v2.0 #22

@piotrwitek

Description

@piotrwitek

Road to v2.0: New API

I'm working on New API using the new language feature (conditional types) coming to TypesScript in the next release (v2.8)

I have already published working version that is compatible with recent typescript@rc release

> to install yarn add typesafe-actions@next

Some changes are inspired by the suggestion from the past and I would really appreciate some feedback from their authors:

The implementation of a new API is taking place on the next branch, and will be released on the release of TypeScript v2.8.

all of the examples are working and have test's against them so they should be rock solid

Finished

ActionsUnion

conditional mapped type, will provide much cleaner and experience to get Union Type of actions

import { ActionsUnion } from 'typesafe-actions';
import * as actions from './todos-duck-module';

type RootAction = ActionsUnion<typeof actions>;
  • ActionsUnion works nicely with nested actions from new async action creator (check below)
const actions = {
    async: buildAction('GET_USER').async<number, { name: string }, string>(),
};
type RootAction = ActionsUnion<typeof actions>;
// { type: "GET_USER" & "REQUEST"; payload: number; } | { type: "GET_USER" & "SUCCESS"; payload: { name: string; }; } | { type: "GET_USER" & "FAILURE"; payload: string; }
  • It also works recursively for deeply nested actions
const actions2 = {
      very: { deep: { empty: buildAction('INCREMENT').empty() } },
      payload: buildAction('ADD').payload<number>(),
};
type RootAction = ActionsUnion<typeof actions2>;
// { type: "INCREMENT"; } | { type: "ADD"; payload: number; }

ReturnType

getting type declaration of action will now be possible to derive from action creator on call site without the need to declare tons of boilerplate before-hand:

import { ReturnType, createAction } from 'typesafe-actions';

const increment = createAction('INCREMENT');

function incrementReducer(state: State, action: ReturnType<typeof increment>) {
...

buildAction

import { buildAction } from 'typesafe-actions'

function will accept action type and return an object with multiple methods to create different action creators described below:

  • buildAction('INCREMENT').empty()
const increment = buildAction('INCREMENT').empty();
expect(increment()).toEqual({ type: 'INCREMENT' }); //  { type: 'INCREMENT' }
expect(getType(increment)).toBe('INCREMENT'); // 'INCREMENT'
  • buildAction('INCREMENT').payload<P>()
const add = buildAction('ADD').payload<number>();
expect(add(10)).toEqual({ type: 'ADD', payload: 10 }); // { type: 'ADD'; payload: number }
expect(getType(add)).toBe('ADD'); // 'ADD'
  • buildAction('INCREMENT').async<R, S, F>()
type User = { name: string };
const fetchUsers = buildAction('LIST_USERS').async<void, User[], string>();
expect(fetchUsers.request())
    .toEqual({ type: 'LIST_USERS_REQUEST' }); // { type: 'LIST_USERS' & 'REQUEST' }
expect(fetchUsers.success([{ name: 'Piotr' }]))
    .toEqual({ type: 'LIST_USERS_SUCCESS', payload: [{ name: 'Piotr' }] }); // { type: 'LIST_USERS' & 'SUCCESS'; payload: User[] }
expect(fetchUsers.failure('error message'))
    .toEqual({ type: 'LIST_USERS_FAILURE', payload: 'error message' }); // { type: 'LIST_USERS' & 'FAILURE'; payload: string }
expect(getType(fetchUsers.request)).toBe('LIST_USERS_REQUEST'); // 'LIST_USERS' & 'REQUEST'
expect(getType(fetchUsers.success)).toBe('LIST_USERS_SUCCESS'); // 'LIST_USERS' & 'SUCCESS'
expect(getType(fetchUsers.failure)).toBe('LIST_USERS_FAILURE'); // 'LIST_USERS' & 'FAILURE'
  • buildAction('NOTIFY').fsa<P>(payloadCreator, metaCreator)

API compatible with redux-actions

const notify = buildAction('NOTIFY').fsa<{ username: string; message?: string }>(
    ({ username, message }) => `${username}: ${message || ''}`,
    ({ username, message }) => ({ username, message })
); // { type: 'NOTIFY'; payload: string; meta: { username: string; message?: string }; }
...

Breaking changes

🎉NO BREAKING CHANGES 🎉

Known Issues

Migration to 1.2.0

Here is an migration example of example in the docs:

  • simple payload action
// current v1.0
const add = createAction('ADD',
  (amount: number) => ({ type: 'ADD', payload: amount }),
);

// migration to v1.2 (complete type-safety is still preserved)
const add = buildAction('ADD').payload<number>;
  • advanced Flux Standard Action
// current v1.0
const notify = createAction('NOTIFY',
  (username: string, message?: string) => ({
    type: 'NOTIFY',
    payload: `${username}: ${message || ''}`,
    meta: { username, message },
  }),
);
// migration to v1.2 (complete type-safety is still preserved)
const notify = buildAction('NOTIFY').fsa<{ username: string; message?: string }>(
    ({ username, message }) => `${username}: ${message || ''}`,
    ({ username, message }) => ({ username, message })
);

Metadata

Metadata

Assignees

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions