# 使用 Typescript 寫 xstate 相關筆記 ###### tags: `xstate` 此筆記始於重構某 js 專案,想慢慢改寫成 typescript,把 xstate 相關筆記集中於此。 ## 定義 service function 參數 `event` 型別 :::info xstate version: 4.13.0 ::: 假設有一個 machine,預期會接收以下 event (`SomeMachineEvent`): ```typescript= enum SomeMachineEventTypeEnum { AAA = 'AAA', BBB = 'BBB' } type SomeMachineEvent = | { type: SomeMachineEventTypeEnum.AAA } | { type: SomeMachineEventTypeEnum.BBB, value: string } ``` 在定義 service function (`handleBBB`) 時, 預期只有觸發 `SomeMachineEventTypeEnum.BBB` event 情況才會執行此 service, 直覺上會想這樣寫: ```typescript= const machine = Machine(config, { services: { handleBBB: (context, event: SomeMachineEvent & { type: SomeMachineEventTypeEnum.BBB }) => { // do something } } }) ``` 但因 xstate 對於 service function 的型別有嚴格規定,不允許這樣定義型別, xstate 作者有提供[一種做法](https://github.com/davidkpiano/xstate/discussions/1591)解決這問題: ```typescript= function assertEventType<TE extends EventObject, TType extends TE["type"]>( event: TE, eventType: TType ): asserts event is TE & { type: TType } { if (event.type !== eventType) { throw new Error( `Invalid event: expected "${eventType}", got "${event.type}"` ); } } ``` ```diff= setClaims: assign({ claims: (_ctx, evt) => { + assertEventType(evt, "LOGIN"); return evt.value; } }) ``` 此做法其實相當於直接寫 if-else statement 告訴 typescript eventObject 是什麼型別, 如果不是對應的型別,直接 throw error 中斷流程。 但筆者不喜歡用 runtime 檢查,於是改寫另一種作法: ```typescript= type AssertEventType<EventType extends SomeMachineEventTypeEnum> = SomeMachineEvent & { type: EventType } ``` ```typescript= const machine = Machine(config, { services: { handleBBB: (context, event) => { const { value } = event as AssertEventType<SomeMachineEventTypeEnum.BBB> // do something with value } } }) ``` ### 後續 重構過程中發現有可能需要在 `SomeMachineEvent` 加入從 `invoke.onDone` 事件傳來的 eventObject 型別 `DoneInvokeEvent<TData>` (from xstate),所以後來又在擴充 `AssertEventType` 加入判別是否為 `DoneInvokeEvent` 的功能。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up