You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XState/FSM中TState与InitEvent类型定义的TypeScript兼容问题

问题

刚上手@xstate/fsm,声明TState类型时碰到TypeScript兼容性问题,相关代码如下:

import {createMachine, EventObject, StateMachine} from '@xstate/fsm';
import {Typestate} from "@xstate/fsm/lib/types";

export interface InitContext {
  retries: number;
}

export interface InitState extends Typestate<InitContext> {
  value: any;
  context: InitContext;
}

export type InitEvent =
    | { type: "LOG_IN" }
    | { type: "FETCHED" }
    | { type: "ERROR" }
    | { type: "RESTART" };
// 重复定义的InitEvent
export type InitEvent =
| { type: "LOG_IN" }
| { type: "FETCHED" }
| { type: "ERROR" }
| { type: "RESTART" }

machine!: StateMachine.Machine<InitContext, InitEvent, InitState>;
machine2!: StateMachine.Machine<InitContext, EventObject, { value: any; context: InitContext }>;

this.machine = createMachine<InitContext>({}); // 此代码报错
this.machine2 = createMachine<InitContext>({}); // 此代码正常运行

报错信息:

TS2322: Type 'Machine<InitContext, EventObject, { value: any; context: InitContext; }>' is not assignable to type 'Machine<InitContext, InitEvent, InitState>'.   
Types of property 'transition' are incompatible.     
Type '(state: string | State<InitContext, EventObject, { value: any; context: InitContext; }>, event: string | EventObject) => State<...>' is not assignable to type '(state: string | State<InitContext, InitEvent, InitState>, event: "FETCH_CONFIG" | "LOG_IN" | "FETCH_MAIN_DATA" | "FETCH_LINKED_DATA" | "FETCHED" | "ERROR" | "RESTART" | InitEvent) => State<...>'.       
Types of parameters 'state' and 'state' are incompatible.         
Type 'string | State<InitContext, InitEvent, InitState>' is not assignable to type 'string | State<InitContext, EventObject, { value: any; context: InitContext; }>'.           
Type 'State<InitContext, InitEvent, InitState>' is not assignable to type 'string | State<InitContext, EventObject, { value: any; context: InitContext; }>'.             
Type 'State<InitContext, InitEvent, InitState>' is not assignable to type 'State<InitContext, EventObject, { value: any; context: InitContext; }>'.
Types of property 'actions' are incompatible.                 
Type 'ActionObject<InitContext, InitEvent>[]' is not assignable to type 'ActionObject<InitContext, EventObject>[]'.
Type 'ActionObject<InitContext, InitEvent>' is not assignable to type 'ActionObject<InitContext, EventObject>'.
Type 'EventObject' is not assignable to type 'InitEvent'.

createMachine方法定义:

export declare function createMachine<TContext extends object, TEvent extends EventObject = EventObject, TState extends Typestate<TContext> = {
    value: any;
    context: TContext;
}>(fsmConfig: StateMachine.Config<TContext, TEvent, TState>, implementations?: {
    actions?: StateMachine.ActionMap<TContext, TEvent>;
}): StateMachine.Machine<TContext, TEvent, TState>;

请问应如何正确定义InitState和InitEvent类型?


解决方案

问题根源

  1. InitEvent被重复定义,属于冗余代码
  2. InitState继承Typestate<InitContext>后重复声明context和value,且value用any会丢失类型约束
  3. 调用createMachine时只传递了TContext类型参数,导致TEvent和TState使用默认值(EventObject和默认Typestate),和声明的machine类型不匹配

修正步骤

  1. 移除重复的InitEvent定义:保留一份即可,确保事件类型是EventObject的子集(当前定义已满足,每个事件都包含type属性)
  2. 正确定义InitState:Typestate<TContext>已包含context: TContext和value字段,只需指定value的具体状态值类型(替换any)
  3. 调用createMachine时传递完整类型参数:若需显式指定类型,需传递TContext、TEvent、TState三个参数;也可让TypeScript根据状态机配置自动推导

修正后的完整代码

import { createMachine, EventObject, StateMachine } from '@xstate/fsm';
import { Typestate } from "@xstate/fsm/lib/types";

export interface InitContext {
  retries: number;
}

// 定义具体的状态值类型,替换any
type InitStateValue = 'idle' | 'loggingIn' | 'success' | 'error';

// 正确继承Typestate,无需重复声明context和value
export interface InitState extends Typestate<InitContext> {
  value: InitStateValue;
}

// 只保留一份InitEvent定义
export type InitEvent =
  | { type: "LOG_IN" }
  | { type: "FETCHED" }
  | { type: "ERROR" }
  | { type: "RESTART" };

// 声明机器类型
machine!: StateMachine.Machine<InitContext, InitEvent, InitState>;

// 调用createMachine时传递完整的三个类型参数,同时补充状态机配置
this.machine = createMachine<InitContext, InitEvent, InitState>({
  initial: 'idle',
  context: { retries: 0 },
  states: {
    idle: {
      on: { LOG_IN: 'loggingIn' }
    },
    loggingIn: {
      on: {
        FETCHED: 'success',
        ERROR: 'error'
      }
    },
    success: {
      on: { RESTART: 'idle' }
    },
    error: {
      on: { RESTART: 'idle' }
    }
  }
});

额外说明

  • 若不需要显式指定InitState,可让TypeScript根据配置自动推导TState,此时调用createMachine只需传递TContext和TEvent:
    this.machine = createMachine<InitContext, InitEvent>({
      // 状态机配置
    });
    
  • 避免用any作为value类型,才能利用TypeScript的类型检查确保状态转换的正确性

内容的提问来源于stack exchange,提问作者Leos Literak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 22:55:18