#Type mismatch for state.can()

1 messages · Page 1 of 1 (latest)

river egret
#

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?

pearl elm
#

How are you typing your events and pass the type to your machine?

river egret
#

createMachine<Context, EventTypes, TypeState>()

#

I also get the same problem in another machine with Machine<Context, any, EventTypes>

pearl elm
#

Yeah I can see state.can infers the type of the whole event object. @flat reef

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)
}
river egret
#

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.

flat reef
#

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

pearl elm
flat reef
#

there are cases when the payload is known up front though

#

for example we use this in the Studio to disable/enable the buttons for zooming