Hi, if I have an event { type: "A", payload: T }, I get a compiler error for state.can("A") but not for state.can({ type: "A", payload: data }) . But in the docs both are supported. (And the payload is not always defined, so I have to add a guard also data && state.can({ type: "A", payload: data }). Am I doing something wrong, or is this a shortcoming in the type definitions?
#Type mismatch for state.can()
1 messages · Page 1 of 1 (latest)
How are you typing your events and pass the type to your machine?
createMachine<Context, EventTypes, TypeState>()
I also get the same problem in another machine with Machine<Context, any, EventTypes>
Yeah I can see state.can infers the type of the whole event object. @flat reef
.can might call your guards - as they are supposed to be pure anyway
therefore if your event is defined as { type: 'A', payload: T } you can't just call state.can('A'), it wouldnt be safe for the runtime as you could end up with accidental runtime exceptions
even if your event is defined as { type: 'A' }, my motto is: don't try to use cute code 😛 you can use state.can({ type: 'A' }) over state.can('A'), it works in the very same way but it won't ever bring you any type problems, whereas .can('A') is way harder to type, so if you ever end up wrapping this in a function, accept an from the outside, like:
function myCan(ev: ??) {
return state.can(ev)
}
what do you even put ?? here? if the type is just EventTypes, and you always define your types like that:
type MyEvents = { type: 'A', payload: T } | { type: 'B' }
you don't defined them like
type MyEvents = { type: 'A', payload: T } | 'B'
then if the accepted argument of .can is just TEvent then you can just easily annotate this, without going through some mental hoops with overly advanced TypeScript:
function myCan(ev: MyEvents /* 🎉 */) {
return state.can(ev)
}
I wasn't aware the guards were called, then it makes perfect sense. I was a bit confused by the docs here: https://xstate.js.org/docs/guides/states.html#state-methods-and-properties, the event DO_SOMETHING has data in one case, and not in the other, so I assumed both were allowed for that event. Of course, the example isn't typed at all, so it's underdefined, but should perhaps be communicated somehow.
I generally always use service.send({type: "A"}) because of better typescript support, but I just learned about state.can() and wasn't aware of the nuances here. But I guess I really can't send an event with an obligatory payload if the payload isn't defined.
But I guess I really can't send an event with an obligatory payload if the payload isn't defined.
yeah, you could mock it out or something to satisfy,can- but ye, i feel like conceptually this is a little bit iffy, what do u really want to test and why if your payload isn't ready?
We probably could merge a PR to the docs that would change those .can calls to use the full object syntax
Just opened a PR to the docs about this part.