我正在将 XState(5.18.2) 与 TS 一起使用。有些事件类型需要额外的参数,有些则不需要。操作使用事件类型,但似乎不区分哪个操作使用哪些事件。
这里简单重现:
import { setup } from 'xstate'
const exampleMachine = setup({
types: {
events: {} as {
| { type: 'eventWithNoParams' }
| { type: 'eventWithParams', meta: string }
}
},
actions: {
doSomething({context, event}, params) {
event.meta // typeError: Property 'meta' does not exist on type { type: 'eventWithNoParams' } | { type: 'eventWithParams', meta: string }
params // is type unknown
}
}
}).createMachine({
id: 'example',
initial: 'start',
states: {
start: {
on: {
'eventWithNoParams': 'nextState'
}
},
nextState: {
on: {
'eventWithParams': 'nextState'
actions: 'doSomething'
}
},
finalState: {
type: 'final'
}
}
})
const actor = createActor(exampleMachine)
actor.start()
actor.send({type: 'eventWithNoParams'}) // state -> nextState
actor.send({type: 'eventWithParams', meta: 'someData' }) // state -> finalState
代码工作正常,并且操作运行正常,但我在操作定义中遇到错误并且类型安全性为零。我不知道如何确保某些操作仅使用某些事件类型。我在文档中找不到任何示例,其中包含带有额外参数的多个事件以及多个操作的键入示例。
相关链接:
这里提出了类似的问题:XState:属性“操作”的类型不兼容但在 v4 中,它具有完全不同的类型系统和语法。
经过更多阅读和不和谐的一些帮助后,我已经弄清楚了。正确的语法是:
import { setup } from 'xstate'
const exampleMachine = setup({
types: {
events: {} as {
| { type: 'eventWithNoParams' }
| { type: 'eventWithParams', meta: string }
}
},
actions: {
doSomething({context, event}, params: { meta: string }) {
params.meta // simply retype param
}
}
}).createMachine({
id: 'example',
initial: 'start',
states: {
start: {
on: {
'eventWithNoParams': 'nextState'
}
},
nextState: {
on: {
'eventWithParams': 'nextState'
actions: {
type: 'doSomething'
params: ({ event }) => ({ meta: event.meta }) // pass param here from event. You'll get type inference in event here from the types up top and type inference from the action param typing.
}
}
},
finalState: {
type: 'final'
}
}
})
const actor = createActor(exampleMachine)
actor.start()
actor.send({type: 'eventWithNoParams'}) // state -> nextState
actor.send({type: 'eventWithParams', meta: 'someData' }) // state -> finalState
有点令人困惑的是,我必须输入两次,但我认为这是有道理的,因为任何
event
都可能触发任何 action
。对于更大的有效负载,可以将类型提取为类型或接口,以获得更好的可重用性。
...
interface Payload { meta: string }
...
types: {
events: {} as {
| { type: 'eventWithNoParams' }
| ({ type: 'eventWithParams' } & Payload)
...
doSomething({context, event}, params: Payload)
...