Description
Using sendParent with types (TEvent) constraints type to the events of the child.
TodosMachineEvent != TodoMachineEvent
Expected Result
sendParent should be constraint to send parent events, event though the ExprWithMeta event should be of type of the child
deleted: {
onEntry: sendParent<TodoMachineContext, TodoMachineEvent, TodosMachineEvent>((ctx: TodoMachineContext, e: TodoMachineEvent):TodosMachineEvent => ({ type: "TODO.DELETE", id: ctx.id }))
}
Actual Result
This is not assignable to todoMachine in TodoMVC example, sendParent expects the TEvent to be the type of the child machine event
deleted: {
onEntry: sendParent<TodoMachineContext,TodosMachineEvent>(ctx => ({ type: "TODO.DELETE", id: ctx.id }))
}
Reproduction
This is a simplified version with the templates
https://codesandbox.io/s/xstate-typescript-error-send-parent-jpzvn
import { Machine, interpret, spawn, assign, sendParent } from "xstate";
// import "./styles.css";
document.getElementById("app").innerHTML = `
<h1>XState TypeScript Example</h1>
<div>
Open the <strong>Console</strong> to view the machine output.
</div>
`;
interface ChildContext {}
interface ChildEvent {
type: "CHILD";
}
const child = Machine<ChildContext, any, ChildEvent>({
id: "child",
initial: "start",
states: {
start: {
onEntry: [sendParent({ type: "PARENT" })]
// ^ the problem (1)
}
}
});
// Edit your machine(s) here
interface MachineContext {
count: number;
childRef?: any;
}
type MachineEvent = { type: "PARENT" } | { type: "TOGGLE" };
const machine = Machine<MachineContext, any, MachineEvent>({
id: "machine",
initial: "start",
context: {
count: 0
},
states: {
start: {
on: {
TOGGLE: "inactive"
}
},
inactive: {
onEntry: [],
on: {
TOGGLE: {
target: "active",
actions: [
() => console.log("hi"),
assign<MachineContext>({
childRef: spawn(child)
// ^ some other problem (2)
})
]
}
}
},
active: {
on: {
TOGGLE: "inactive"
}
}
}
});
// Edit your service(s) here
const service = interpret(machine).onTransition(state => {
console.log(state.value);
});
service.start();
service.send("TOGGLE");
service.send("TOGGLE");
(1) the error is:
Type 'SendAction<ChildContext, { type: "PARENT"; }>[]' is not assignable to type 'SingleOrArray<Action<ChildContext, ChildEvent>>'.
Type 'SendAction<ChildContext, { type: "PARENT"; }>[]' is not assignable to type 'Action<ChildContext, ChildEvent>[]'.
Type 'SendAction<ChildContext, { type: "PARENT"; }>' is not assignable to type 'Action<ChildContext, ChildEvent>'.
Type 'SendAction<ChildContext, { type: "PARENT"; }>' is not assignable to type 'string'.ts(2322)
types.d.ts(267, 5): The expected type comes from property 'onEntry' which is declared here on type 'StateNodeConfig<ChildContext, any, ChildEvent>'
(2) Also, In this example I get this warning in the console
Warning: Attempted to spawn an Actor (ID: "child") outside of a service. This will have no effect.
Not sure what is that, I understand that service.start(); should be done, and it is, so i'm not sure of the problem here
Additional context
xstate@^4.7.0-rc3
As a workaround, I just put any as the event type for sendParent (second type argument).
Have a look at line 75 / notifyClientV4 in machines.ts (only version without an error).
https://codesandbox.io/s/inspiring-newton-3ywkw
So in the example above, you could do this:
start: {
onEntry: [sendParent<ChildContext, any>({ type: "PARENT" })]
// ^ Shouldn't be a problem anymore
}
EDIT: sidenote, this is how I normally "fix" your second issue
import { Interpreter } from 'xstate';
Interface ChildStateSchema {...}
// ...
type ChildActor = Interpreter<ChildContext, ChildStateSchema, ChildEvent>;
// ...
actions: [
() => console.log("hi"),
assign<MachineContext>({
childRef: spawn(child) as ChildActor
// ^ some other problem should be ok now
})
Is someone working on this? Or may I help with that?
Thank you 馃挍
This is great! I'm declaring a wrapper for the child machine so I only have to type the generics once:
import { sendParent as sendParentX } from 'xstate'
// ... other imports and declarations for parent/child types
const sendParent = (e: ParentEvent | ParentEvent["type"]): ReturnType<typeof sendParentX> =>
sendParentX<ChildContext, ChildEvent, ParentEvent>(e)
Most helpful comment
This is great! I'm declaring a wrapper for the child machine so I only have to type the generics once: