elm-types Strawman

import t from 'elm-types';

const AddTodo = t.Constructor('AddTodo', [t.String]);
const RemoveTodo = t.Constructor('AddTodo', [t.String]);
const Action = t.Union('Action', [AddTodo]);
const Todo = t.Record('Todo', {
    name: t.String,
    completed: t.Bool
});
const Model = t.Record('Model', {
    todos: t.List('todos', Todo)
});

// Throws if function is not passed correct parameter types
const update = t.Function('update', [Model, Action, Model], (state, action) =>
    // Throws if last object is not exaustive
    t.case(action, Action, {
        [AddTodo]: add => ({
            ...state,
            todos: [...state.todos, Todo(add, false)]
        }),
        [RemoveTodo]: remove => ({
            ...state,
            todos: state.todos.filter(todo => todo.name === remove)
        })
    });
);

const store = createStore(update, Model([]));

const App = ({ todos }) => (
    <div>
        <h1>Todo List</h1>
        <ul>
            {todos.map(todo => (
                <li onClick={() => store.dispatch(RemoveTodo(todo.name))}>
                    {todo.name}
                </li>
            ))}
        </ul>
    </div>
);

const AppProps = t.Record('AppProps', {
    todos: t.List('todos', Todo)
});

App.propTypes = t.PropTypes(AppProps);

render(
    <App {...store.getState()} />,
    document.getElementById('app')
);