statelyai / xstate-tools

Public monorepo for XState tooling

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Values from `state` and `service` stores in `useMachine` hook are different in types

alexamy opened this issue · comments

Hi! Next examples will be in Svelte, but React version has the same problem too.

I should mention that I am confused with distinction between service and state values from useMachine hook, it would be great to someone explain it or point to specific part of the docs about it.

Problem

Looks like $state and $service values, being referentially equal, may be different in types.

Setup

machine.ts:

import { createMachine } from 'xstate';

export const machine = createMachine({
  initial: 'main',
  tsTypes: {} as import("./machine.typegen").Typegen0,
  schema: {
    events: {} as { type: 'event' },
    actions: {} as { type: 'action' },
  },
  states: { main: {} },
  on: {
    'event': { actions: ['action'] },
  }
});

App.svelte:

<script lang="ts">
  import { useMachine } from '@xstate/svelte';
  import type { StateFrom } from 'xstate';
  import { machine } from './machine';

  const { state, service } = useMachine(machine, {
    actions: {
      'action': () => {},
    }
  });

  function stateIdentity(state: StateFrom<typeof machine>) {
    return state;
  }

  const state1 = stateIdentity($state);
  const state2 = stateIdentity($service); // types not ok
</script>

<!-- equals true, but ts complains -->
state === service: <b>{$state === $service}</b>

Expected

Typescript treats $state and $service as the same.

Actual

Typescript complains about $service value.

Workaround

Move action implementation in a machine definition, like here.

Repro

https://github.com/alexamy/xstate-typegen-state-from-mismatch

Typescript warning

Argument of type 'State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, MarkAllImplementationsAsProvided<ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>>' is not assignable to parameter of type 'State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>'.
  The types of 'machine.getStateNodes' are incompatible between these types.
    Type '(state: StateValue | State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, MarkAllImplementationsAsProvided<ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>>) => StateNode<...>[]' is not assignable to type '(state: StateValue | State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>) => StateNode<...>[]'.
      Types of parameters 'state' and 'state' are incompatible.
        Type 'StateValue | State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>' is not assignable to type 'StateValue | State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, MarkAllImplementationsAsProvided<ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>>'.
          Type 'State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>' is not assignable to type 'StateValue | State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, MarkAllImplementationsAsProvided<ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>>'.
            Type 'State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>' is not assignable to type 'State<unknown, { type: "event"; }, any, { value: any; context: unknown; }, MarkAllImplementationsAsProvided<ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>>'.
              The types of 'machine.__TResolvedTypesMeta' are incompatible between these types.
                Type 'ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>' is not assignable to type 'MarkAllImplementationsAsProvided<ResolveTypegenMeta<Typegen0, { type: "event"; }, BaseActionObject, ServiceMap>>'.

Use case

I want to make typesafe children lookup, which is not typed as far as I know. Example:

function getActor(state: StateFrom<typeof machine>) {
  const actor = state.children.someActor;
  if(!actor) throw new Error('Some actor is not defined or spawned yet.');

  return actor as unknown as InterpreterFrom<typeof actorMachine>;
}