So I've got this classic switch case redux reducer in a todomvc that I want to make functional but can't seem to wrap my head around ts typings for that.
Switch case works great for pattern matching and narrows down action discriminated union by type. But I don't seem to get how to pass around narrowed actions with a functional approach where object literal's key should do a type narrowing.
What I got so far is union type of all functions and some ts errors by the way. Would really appreciate any help on the matter to get a better idea how to use strict types with ts.
import { action as actionCreator } from 'typesafe-actions';
import uuid from 'uuid';
import { ITodo } from 'types/models';
const ADD_TODO = 'todos/ADD_TODO';
const TOGGLE_ALL = 'todos/TOGGLE_ALL';
const REMOVE_TODO = 'todos/REMOVE_TODO';
export const addTodo = (title: string) => actionCreator(ADD_TODO, { title });
export const removeTodo = (id: string) => actionCreator(REMOVE_TODO, { id });
export const toggleAll = (checked: boolean) =>
actionCreator(TOGGLE_ALL, { checked });
type TodosAction =
| ReturnType<typeof addTodo>
| ReturnType<typeof removeTodo>
| ReturnType<typeof toggleAll>;
type TodosState = ReadonlyArray<ITodo>;
// no idea what typings should be
const switchCase = <C>(cases: C) => <D extends (...args: any[]) => any>(
defaultCase: D
) => <K extends keyof C>(key: K): C[K] | D => {
return Object.prototype.hasOwnProperty(key) ? cases[key] : defaultCase;
};
export default function(
state: TodosState = [],
action: TodosAction
): TodosState {
// union type of 4 functions
const reducer = switchCase({
// (parameter) payload: any
// How do I get types for these?
[ADD_TODO]: payload => [
...state,
{
completed: false,
id: uuid.v4(),
title: payload.title,
},
],
[REMOVE_TODO]: payload => state.filter(todo => todo.id !== payload.id),
[TOGGLE_ALL]: payload =>
state.map(todo => ({
...todo,
completed: payload.checked,
})),
})(() => state)(action.type);
// [ts] Cannot invoke an expression whose type lacks a call signature. Type
// '((payload: any) => { completed: boolean; id: string; title: any; }[]) |
// ((payload: any) => ITodo[...' has no compatible call signatures.
return reducer(action.payload);
}