#Type narrowing with assertEvent on partial wildcard events

1 messages · Page 1 of 1 (latest)

maiden trench
#

I'm using partial wildcard transitions as described in the docs here https://stately.ai/docs/transitions#partial-wildcard-transitions.

What is the recommended way to type events so that, inside actions for a wildcard transition, the event parameter is narrowed to only the matching events?

let's say I have an event like this

type FeedbackEvent =
  | { type: 'FEEDBACK.*`; message: string }

Then I want type safety in my actions. I believe the inline case here works great without any assertEvent at all, but if I were to move it to a named action, I probably want some type narrowing.

      on: {
        'FEEDBACK.*': {
          actions: [
            ({ event }) => {
              // How can I get `event` narrowed to `{ type: `feedback.${string}`; message: string }` here?
              console.log(event.message);
            },
          ],
        },
      },

A transition is a change from one finite state to another, triggered by an event.

bleak knot
#

This should already work naturally:

#
const machine = setup({
  types: {
    events: {} as
      | {
          type: 'FEEDBACK.MESSAGE';
          message: string;
        }
      | {
          type: 'FEEDBACK.RATE';
          rate: number;
        }
  }
}).createMachine({
  on: {
    'FEEDBACK.*': {
      actions: ({ event }) => {
        if (event.type === 'FEEDBACK.MESSAGE') {
          event.message satisfies string;
          // @ts-expect-error
          event.rate satisfies number;
        } else {
          event.rate satisfies number;
          // @ts-expect-error
          event.message satisfies string;
        }
      }
    }
  }
});
#

And assertEvent works too still:

  on: {
    'FEEDBACK.*': {
      actions: ({ event }) => {
        assertEvent(event, 'FEEDBACK.MESSAGE');

        event.message satisfies string;
      }
    }
  }
maiden trench
#

As far as I understand, this would fail on FEEDBACK.RATE

  on: {
    'FEEDBACK.*': {
      actions: ({ event }) => {
        assertEvent(event, 'FEEDBACK.MESSAGE');

        event.message satisfies string;
      }
    }
  }

And it gets even more tricky when you don't know which exact event you'll receive.

FEEDBACK.{ID}

Like FEEDBACK.90as98d7a8s1asdk8

Like I would love an assertEvent(event, 'FEEDBACK.*');but I assume that's quite tricky to accomplish?

for now I'm defining my own type guard

const isFeedbackEvent = (
  event: FeedbackEvent,
): event is `FEEDBACK_EVENT.${string}` => {
  return event.type.startsWith(
    'FEEDBACK.',
  );
};