Project / 2020 / earlier project
FSM
A fully typed finite state machine for JavaScript and TypeScript.
State machines are easy to sketch and annoying to keep honest once events, delayed work, and subscriptions accumulate. I wanted the compiler to catch the wiring mistakes before the machine ran.
This small library uses TypeScript’s type system to tie states, events, and handlers together. It also supports entry handlers, delayed handlers, and state-change subscriptions.
A machine the compiler can see
This traffic light moves itself forward on a timer. Every state name and every tick handler belongs to the machine’s declared types.
type Red = DefineState<'red'>;
type Yellow = DefineState<'yellow'>;
type Green = DefineState<'green'>;
type Tick = DefineEvent<'tick'>;
const trafficLight = Service.define<Red | Yellow | Green, Tick>(
(machine) => machine
.defineState('red', (state) => state
.onEnter((ctx) => ctx.runAfter(30_000, (ctx) => ctx.send('tick')))
.onEvent('tick', (ctx) => ctx.transitionTo('green')))
.defineState('green', (state) => state
.onEnter((ctx) => ctx.runAfter(60_000, (ctx) => ctx.send('tick')))
.onEvent('tick', (ctx) => ctx.transitionTo('yellow')))
.defineState('yellow', (state) => state
.onEnter((ctx) => ctx.runAfter(10_000, (ctx) => ctx.send('tick')))
.onEvent('tick', (ctx) => ctx.transitionTo('red'))),
'red',
);
Change 'green' to an undeclared state or send an undeclared event and the type checker objects at the transition, not after deployment.