CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-xstate

Actor-based state management & orchestration for complex app logic.

Overview
Eval results
Files

guards.mddocs/

Guards

Guard functions and combinators for conditional logic in state machine transitions and actions. Guards provide type-safe predicate functions that determine whether transitions should be taken or actions should be executed.

Capabilities

Guard Predicates

Core guard predicate functions for evaluating conditions based on context and events.

/**
 * Function type for guard predicates that evaluate conditions
 * @param args - Guard arguments containing context and event
 * @param params - Optional parameters passed to the guard
 * @returns Boolean indicating whether the guard condition is met
 */
type GuardPredicate<
  TContext,
  TExpressionEvent extends EventObject,
  TParams = undefined,
  TGuard = any
> = {
  (args: GuardArgs<TContext, TExpressionEvent>, params: TParams): boolean;
  _out_TGuard?: TGuard;
};

interface GuardArgs<TContext, TExpressionEvent extends EventObject> {
  /** Current machine context */
  context: TContext;
  /** Current event being processed */
  event: TExpressionEvent;
}

type Guard<TContext, TExpressionEvent extends EventObject, TParams, TGuard> =
  | GuardPredicate<TContext, TExpressionEvent, TParams, TGuard>
  | { type: string; params?: TParams }
  | string;

type UnknownGuard = Guard<any, any, any, any>;

Logical Combinators

Logical operators for combining multiple guards into complex conditional logic.

/**
 * Logical AND guard combinator - evaluates to true if all guards evaluate to true
 * @param guards - Array of guards to combine with AND logic
 * @returns GuardPredicate that returns true when all guards are true
 */
function and<TContext, TExpressionEvent extends EventObject>(
  guards: Guard<TContext, TExpressionEvent, any, any>[]
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;

/**
 * Logical OR guard combinator - evaluates to true if any guard evaluates to true
 * @param guards - Array of guards to combine with OR logic
 * @returns GuardPredicate that returns true when any guard is true
 */
function or<TContext, TExpressionEvent extends EventObject>(
  guards: Guard<TContext, TExpressionEvent, any, any>[]
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;

/**
 * Logical NOT guard combinator - evaluates to true if the guard evaluates to false
 * @param guard - Guard to negate
 * @returns GuardPredicate that returns the opposite of the input guard
 */
function not<TContext, TExpressionEvent extends EventObject>(
  guard: Guard<TContext, TExpressionEvent, any, any>
): GuardPredicate<TContext, TExpressionEvent, unknown, any>;

Usage Examples:

import { and, or, not } from "xstate/guards";

// Combine guards with AND logic
and([
  ({ context }) => context.isLoggedIn,
  ({ context }) => context.hasPermission,
  ({ event }) => event.type === "SUBMIT"
])

// Combine guards with OR logic  
or([
  ({ context }) => context.isAdmin,
  ({ context }) => context.isOwner,
  ({ event }) => event.force === true
])

// Negate a guard
not(({ context }) => context.isDisabled)

// Complex combinations
and([
  ({ context }) => context.user != null,
  or([
    ({ context }) => context.user.role === "admin",
    ({ context }) => context.user.role === "moderator"
  ]),
  not(({ context }) => context.user.suspended)
])

State Checking

Guard for checking if the machine is currently in specific states.

/**
 * Guard to check if machine is in specific state(s)
 * @param stateValue - State value to check (string, object, or state ID)
 * @returns GuardPredicate that returns true when machine matches the state
 */
function stateIn<TContext, TExpressionEvent extends EventObject>(
  stateValue: StateValue
): GuardPredicate<TContext, TExpressionEvent, any, any>;

type StateValue = string | { [key: string]: StateValue };

Usage Examples:

import { stateIn } from "xstate/guards";

// Check simple state
stateIn("loading")

// Check nested state
stateIn({ editing: "text" })

// Check multiple possible states
stateIn({ form: { validation: "pending" } })

// Use in transitions
{
  on: {
    SUBMIT: {
      guard: stateIn("ready"),
      target: "submitting"
    }
  }
}

Guard Evaluation

Low-level function for evaluating guards in different contexts.

/**
 * Evaluates a guard against current snapshot and context
 * @param guard - Guard to evaluate (function, object, or string reference)
 * @param context - Current machine context
 * @param event - Current event
 * @param snapshot - Current machine snapshot
 * @returns Boolean result of guard evaluation
 */
function evaluateGuard<TContext, TExpressionEvent extends EventObject>(
  guard: Guard<TContext, TExpressionEvent, any, any>,
  context: TContext,
  event: TExpressionEvent,
  snapshot: AnyMachineSnapshot
): boolean;

Usage Examples:

import { evaluateGuard } from "xstate/guards";

// Evaluate guard manually
const isValid = evaluateGuard(
  ({ context, event }) => context.count > event.threshold,
  { count: 10 },
  { type: "CHECK", threshold: 5 },
  currentSnapshot
);

// Evaluate complex guard
const hasAccess = evaluateGuard(
  and([
    ({ context }) => context.user?.active,
    ({ context }) => context.permissions.includes("read")
  ]),
  context,
  event,
  snapshot
);

Guard Configuration

Inline Guards

Guards defined directly in machine configuration as functions.

// Inline guard function
{
  on: {
    NEXT: { 
      guard: ({ context, event }) => context.step < event.maxSteps,
      target: "nextStep"
    }
  }
}

Named Guards

Guards defined in machine implementations and referenced by name.

const machine = setup({
  guards: {
    canProceed: ({ context }) => context.isReady && context.hasData,
    isAuthorized: ({ context, event }) => {
      return context.user?.permissions?.includes(event.requiredPermission);
    }
  }
}).createMachine({
  on: {
    PROCEED: {
      guard: "canProceed",  // Reference by name
      target: "processing"
    },
    SECURE_ACTION: {
      guard: { 
        type: "isAuthorized", 
        params: { requiredPermission: "admin" }
      },
      target: "adminPanel"
    }
  }
});

Parameterized Guards

Guards that accept parameters for reusable conditional logic.

const machine = setup({
  guards: {
    greaterThan: ({ context }, { value }) => context.count > value,
    hasRole: ({ context }, { role }) => context.user?.role === role,
    withinTimeframe: ({ context }, { hours }) => {
      const now = Date.now();
      const diff = now - context.timestamp;
      return diff < (hours * 60 * 60 * 1000);
    }
  }
}).createMachine({
  on: {
    CHECK_COUNT: {
      guard: { type: "greaterThan", params: { value: 10 } },
      actions: "processHigh"
    },
    ADMIN_ACTION: {
      guard: { type: "hasRole", params: { role: "admin" } },
      actions: "executeAdmin"
    },
    TIME_SENSITIVE: {
      guard: { type: "withinTimeframe", params: { hours: 24 } },
      actions: "handleUrgent"
    }
  }
});

Dynamic Parameters

Guards with parameters resolved at runtime based on context and event.

{
  on: {
    VALIDATE: {
      guard: {
        type: "meetsThreshold",
        params: ({ context, event }) => ({
          threshold: context.dynamicThreshold,
          multiplier: event.urgency || 1
        })
      },
      target: "validated"
    }
  }
}

Advanced Guard Patterns

Multi-Level Conditions

Complex nested guard logic for sophisticated conditional behavior.

import { and, or, not, stateIn } from "xstate/guards";

const complexGuard = and([
  // User must be authenticated
  ({ context }) => context.user != null,
  
  // Must be in correct state
  stateIn({ form: "editing" }),
  
  // Either admin OR (owner AND not suspended)
  or([
    ({ context }) => context.user.role === "admin",
    and([
      ({ context }) => context.user.isOwner,
      not(({ context }) => context.user.suspended)
    ])
  ]),
  
  // Time-based condition
  ({ context }) => {
    const now = Date.now();
    return now - context.lastAction < 300000; // 5 minutes
  }
]);

Form Validation Guards

Common patterns for form validation and user input checking.

const formGuards = {
  // Field validation
  hasValidEmail: ({ context }) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(context.form.email);
  },
  
  hasValidPassword: ({ context }) => {
    const { password } = context.form;
    return password && password.length >= 8;
  },
  
  passwordsMatch: ({ context }) => {
    const { password, confirmPassword } = context.form;
    return password === confirmPassword;
  },
  
  // Complete form validation
  formIsValid: and([
    ({ context }) => context.form.email?.trim().length > 0,
    "hasValidEmail",
    "hasValidPassword", 
    "passwordsMatch"
  ]),
  
  // Submission readiness
  canSubmit: and([
    "formIsValid",
    not(stateIn("submitting")),
    ({ context }) => !context.hasErrors
  ])
};

Permission-Based Guards

Role and permission checking patterns for access control.

const permissionGuards = {
  // Role checks
  isAdmin: ({ context }) => context.user?.role === "admin",
  isModerator: ({ context }) => context.user?.role === "moderator", 
  isOwner: ({ context, event }) => context.user?.id === event.resourceOwnerId,
  
  // Permission checks
  hasPermission: ({ context }, { permission }) => {
    return context.user?.permissions?.includes(permission);
  },
  
  // Complex access control
  canEdit: or([
    "isAdmin",
    and([
      "isOwner",
      ({ context }) => !context.resource?.locked
    ])
  ]),
  
  canDelete: and([
    or(["isAdmin", "isOwner"]),
    ({ context }) => context.resource?.status !== "published"
  ])
};

Type Safety

Guards maintain full type safety with proper TypeScript integration:

interface Context {
  count: number;
  user: { name: string; role: "admin" | "user" } | null;
}

type Events = 
  | { type: "INCREMENT"; amount: number }
  | { type: "CHECK_ROLE"; requiredRole: string };

// Type-safe guard with proper context and event types
const typedGuard: GuardPredicate<Context, Events> = ({ context, event }) => {
  // context and event are properly typed
  if (event.type === "INCREMENT") {
    return context.count + event.amount < 100;
  }
  if (event.type === "CHECK_ROLE") {
    return context.user?.role === event.requiredRole;
  }
  return false;
};

Install with Tessl CLI

npx tessl i tessl/npm-xstate

docs

actions.md

actors.md

graph-utilities.md

guards.md

index.md

state-machines.md

tile.json