A simple and small TypeScript finite state machine
- Complete type safety
- Type inference when registering transitions and dispatching events
- Easily and cheaply create several state machines of the same type
- Returns the
transition
function for registering valid transitions - Returns the
create
factory function which returns a finite state machine object
The function expects a State
and Event
generic.
This is so we can provide type safety and inference for the dispatch
and transition
functions
Create Machine Options
A custom callback can be invoked if an invalid transition occurs by providing onInvalid
when creating a machine:
interface CreateOptions {
name?: string
throw?: boolean
onInvalid?: (from: string, event: string) => void
}
The create
function is a factory function that creates an instance of the finite state machine.
const fsm = createMachine<State, Event>('unlocked', {
onInvalid: (from, event) => {
console.warn(`Invalid state transition ${from}::${event}`)
},
throw: false
})
fsm.transition([...], [...], [...])
const window = fsm.create()
window.dispatch('open')
The transition
function registers valid transitions.
The fourth parameter is an optional callback which is called when the transition is invoked.
Asynchronous state machines only differ by allowing asynchronous callbacks.
type Transition = [State, Event, State, Callback] | [State, Event, State]
function transition(...transition: Transition[])
import { createMachine } from 'fsmachine'
type State = 'opened' | 'unlocked' | 'locked' | 'broken'
type Event = 'open' | 'close' | 'lock' | 'unlock' | 'break'
// "throw: false" disables throwing InvalidTransition errors and returns false instead
const { transition, create } = createMachine<State, Event>('unlocked', {
name: 'window',
throw: false,
})
// These calls are all completely type safe due to the State and Event generics provided earlier
transition(
['locked', 'unlock', 'unlocked', (from, event, to) => console.log({ from, event, to })],
['unlocked', 'open', 'opened'],
['opened', 'close', 'unlocked'],
['unlocked', 'lock', 'locked'],
['locked', 'break', 'broken'],
['unlocked', 'break', 'broken'],
)
// (Optional) We can override the options here or provide nothing to inherit the original options.
const window = create({ name: 'my-first-window', throw: false })
// Invalid state transitions throw by default
// We can disable this behaviour by providing { throw: false } to either createMachine() or to create()
window.dispatch('lock') // returns true
window.getState() // returns 'locked'
window.dispatch('open') // returns false
window.dispatch('break') // returns true
window.getState() // returns 'broken'