TypeScript
Journey is TypeScript-first by design.
Types are not an add-on here. They are part of how you model steps, events, payloads, context, metadata, and machine behavior safely.
Why It Matters
When transitions are typed, flow mistakes are caught earlier:
- invalid step ids
- invalid event names
- wrong payload shapes
- missing context fields in guards/effects
This reduces runtime surprises and makes refactors safer.
Core Type Pattern
import {
createJourneyMachine,
createTransitions,
tx,
type JourneyDefinition
} from "@rxova/journey-core";
type StepId = "start" | "details" | "review";
type Event = "goToNextStep" | "completeJourney" | "requestClose";
type Context = { name: string; dirty: boolean };
type PayloadMap = {
requestClose: { source: "button" | "shortcut" };
};
const journey: JourneyDefinition<Context, StepId, Event, PayloadMap> = {
initial: "start",
context: { name: "", dirty: false },
steps: {
start: {},
details: {},
review: {}
},
transitions: createTransitions(
tx.from("start").on("goToNextStep").to("details"),
tx.from("details").on("goToNextStep").to("review"),
tx.from("review").toComplete()
)
};
const machine = createJourneyMachine(journey);
Event Payload Typing
With PayloadMap, event payload shape is enforced at callsites:
await machine.send({ type: "requestClose", payload: { source: "button" } });
// await machine.send({ type: "requestClose", payload: { source: 123 } }); // type error
React Typing Model
@rxova/journey-react captures types once via bindings:
const bindings = createJourneyBindings(journey);
From there, useJourneyApi, useJourneySnapshot, and useJourneyMachine are already journey-typed, without repeating generics per hook call.
Recommended Team Pattern
- Define shared
StepId,Event, andContexttypes near your journey definition. - Add a payload map once custom events carry structured payloads.
- Keep transition ids stable (
id) for better typed observability/debug logs. - Prefer inference where possible, add explicit generics where clarity helps.