Guards
Guard creators and combinators for conditional transitions.
Guard Type
type Guard<TContext, TEvent> = (params: { context: TContext; event: TEvent;}) => boolean;guard
Creates a named, reusable guard function.
function guard<TContext, TEvent>( predicate: (params: { context: TContext; event: TEvent }) => boolean): Guard<TContext, TEvent>Example:
import { guard } from "effstate";
const isAdmin = guard<MyContext, MyEvent>( ({ context }) => context.user.role === "admin");
const hasBalance = guard<MyContext, TransferEvent>( ({ context, event }) => context.balance >= event.amount);
// Use in transitionon: { TRANSFER: { target: "transferring", guard: hasBalance, },}and
Combines guards with logical AND. Returns true only if all guards return true.
function and<TContext, TEvent>( ...guards: Guard<TContext, TEvent>[]): Guard<TContext, TEvent>Example:
import { and, guard } from "effstate";
const isAdmin = guard(({ context }) => context.user.role === "admin");const isVerified = guard(({ context }) => context.user.verified);
const canModerate = and(isAdmin, isVerified);
on: { DELETE_POST: { target: "deleting", guard: canModerate, },}or
Combines guards with logical OR. Returns true if any guard returns true.
function or<TContext, TEvent>( ...guards: Guard<TContext, TEvent>[]): Guard<TContext, TEvent>Example:
import { or, guard } from "effstate";
const isAdmin = guard(({ context }) => context.user.role === "admin");const isOwner = guard(({ context }) => context.user.id === context.resource.ownerId);
const canEdit = or(isAdmin, isOwner);
on: { EDIT: { target: "editing", guard: canEdit, },}not
Negates a guard. Returns true if the guard returns false.
function not<TContext, TEvent>( guard: Guard<TContext, TEvent>): Guard<TContext, TEvent>Example:
import { not, guard } from "effstate";
const isLocked = guard(({ context }) => context.locked);const isUnlocked = not(isLocked);
on: { EDIT: { target: "editing", guard: isUnlocked, },}Combining Combinators
Combinators can be nested for complex conditions.
import { and, or, not, guard } from "effstate";
const isAdmin = guard(({ context }) => context.user.role === "admin");const isOwner = guard(({ context }) => context.user.id === context.post.authorId);const isPublished = guard(({ context }) => context.post.status === "published");const isDeleted = guard(({ context }) => context.post.deleted);
// Complex permission: (admin OR owner) AND (not published) AND (not deleted)const canEditDraft = and( or(isAdmin, isOwner), not(isPublished), not(isDeleted));
// admin OR (owner AND not published)const canModify = or( isAdmin, and(isOwner, not(isPublished)));Inline Guards
Guards can also be defined inline in transition config:
on: { SUBMIT: { target: "submitting", guard: ({ context }) => context.form.isValid, }, TRANSFER: { target: "transferring", guard: ({ context, event }) => event.amount > 0 && event.amount <= context.balance, },}Multiple Guarded Transitions
Define multiple transitions for the same event with different guards:
on: { SUBMIT: [ { target: "premium", guard: ({ context }) => context.user.tier === "premium", }, { target: "standard", guard: ({ context }) => context.user.tier === "standard", }, { // Fallback - no guard target: "basic", }, ],}The first matching guard wins. If no guards match and there’s no guardless fallback, no transition occurs.
See Also
- Guards Guide - Detailed usage examples
- createMachine - Using guards in transitions