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
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'] },
}
});
<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>;
}