statelyai / xstate

Actor-based state management & orchestration for complex app logic.
https://stately.ai/docs
MIT License
26.97k stars 1.24k forks source link

Bug: types broken with assign #4853

Open rostero1 opened 5 months ago

rostero1 commented 5 months ago

XState version

XState version 5

Description

In the code below, thing is allowed in the RUN transition. If you remove the actions: 'doAction' then it will properly catch the error and not allow thing. This seems to be specific to assign.

In my real world code, I accidentally had type: instead of target: and it took a bit to figure out why my machine wasn't working as expected.

import { setup, assign } from 'xstate';

type Events = { type: 'RUN' };
type Context = {};

setup({
  types: {
    events: {} as Events,
    context: {} as Context,
  },
  actions: {
    doAction: assign(({ context }) => {
      return context;
    }),
  },
}).createMachine({
  initial: 'idle',
  states: {
    idle: {
      on: {
        RUN: { thing: 'stop', actions: 'doAction' },
      },
    },
    stop: {},
  },
});

Expected result

thing is not assignable to type 'TransitionConfigOrTarget`

Actual result

It type checks

Reproduction

https://stackblitz.com/edit/vitejs-vite-9vip7x?file=src%2Fmachine.ts,tsconfig.json&terminal=dev

Additional context

No response

Andarist commented 5 months ago

This has nothing to do with assign. With setup API our whole config is the target of inference - inferred type arguments are exempt from excess property check (which is something between a lint rule and a hard error in TS). We can't do anything about it right now - this is how TS works.

rostero1 commented 5 months ago

I'm such a beginner at typescript, even more so relatively. Why does it catch the following when I remove the actions:

import { setup, assign } from 'xstate';

type Events = { type: 'RUN' };
type Context = {};

setup({
  types: {
    events: {} as Events,
    context: {} as Context,
  },
  actions: {
    doAction: assign(({ context }) => {
      return context;
    }),
  },
}).createMachine({
  initial: 'idle',
  states: {
    idle: {
      on: {
        RUN: { thing: 'stop' },
      },
    },
    stop: {},
  },
});

With Type '{ thing: string; }' is not assignable to type 'TransitionConfigOrTarget<Context, Events, Events, never, { type: "doAction"; params: NonReducibleUnknown; }, never, never, EventObject>'.