Skip to content

Instantly share code, notes, and snippets.

@mmyoji
Last active October 9, 2024 02:40
Show Gist options
  • Save mmyoji/8c6ddde79ae1598494ee11a22d312e79 to your computer and use it in GitHub Desktop.
Save mmyoji/8c6ddde79ae1598494ee11a22d312e79 to your computer and use it in GitHub Desktop.
State machine in TypeScript
/**
* @example
* ```ts
* const POST_STATE = {
* DRAFT: 0,
* PUBLISHED: 1,
* // ...
* } as const;
* type PostState = (typeof POST_STATE)[keyof typeof POST_STATE];
*
* const POST_EVENT = {
* publish: {
* from: POST_STATE.DRAFT,
* to: POST_STATE.PUBLISHED,
* },
* withdraw: {
* from: MY_STATE.PUBLISHED,
* to: MY_STATE.DRAFT,
* },
* // ...
* } as const;
* type PostEvent = keyof typeof POST_EVENT;
*
* const updatePostState = createStateMachine(POST_EVENT);
*
* {
* const next = updatePostState(POST_STATE.DRAFT, "publish");
* //=> 1
* }
*
* {
* const next = updatePostState(POST_STATE.PUBLISHED, "publish");
* //=> Error("Invalid transition: 1 -> 1");
* }
* ```
*/
export function createStateMachine<Event extends string, State extends number | string>(
event: Record<Event, { from: State; to: State }>
): (prev: State, evt: Event) => State {
return (prev, evt) => {
const e = event[evt];
if (e == null) {
throw new Error(`Unknown event: ${evt}`);
}
if (prev !== e.from) {
throw new Error(`Invalid transition: ${prev} -> ${e.to}`);
}
return e.to;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment