4. 使用枚举管理订单状态流转

使用枚举管理订单状态流转 学习卡片

为什么订单状态适合用枚举

订单状态在业务代码中会反复出现:创建订单、支付、发货、确认收货、取消。如果直接写 "pending"、"paid" 这类字符串,拼写错误、大小写不一致、重构改名都会在运行时才暴露。字符串枚举把状态收敛到一处定义,让编译器参与检查。

本案例定义 5 个状态:Pending(待支付)、Paid(已支付)、Shipped(已发货)、Completed(已完成)、Cancelled(已取消)。状态流转规则为:待支付 → 已支付或已取消;已支付 → 已发货;已发货 → 已完成。其余流转一律拒绝。

订单状态流转(合法路径与拒绝路径) Pending 待支付 Paid 已支付 Shipped 已发货 Completed 已完成 Cancelled 已取消 pay() ship() confirm() cancel() 非法流转 → 抛错 不可逆 合法流转 拒绝流转

定义字符串枚举

使用字符串枚举而非数字枚举,序列化到 JSON 或日志时保留可读值,调试时不必反查数字含义。

enum OrderStatus {
  Pending = "PENDING",
  Paid = "PAID",
  Shipped = "SHIPPED",
  Completed = "COMPLETED",
  Cancelled = "CANCELLED",
}

interface Order {
  id: string;
  status: OrderStatus;
}

用枚举成员做分支判断

流转函数接收当前订单和目标状态,先查合法迁移表,再决定是否更新。分支判断全部使用 OrderStatus.Xxx,不出现裸字符串。

const transitions: Record<OrderStatus, OrderStatus[]> = {
  [OrderStatus.Pending]: [OrderStatus.Paid, OrderStatus.Cancelled],
  [OrderStatus.Paid]: [OrderStatus.Shipped],
  [OrderStatus.Shipped]: [OrderStatus.Completed],
  [OrderStatus.Completed]: [],
  [OrderStatus.Cancelled]: [],
};

function transition(order: Order, next: OrderStatus): Order {
  const allowed = transitions[order.status];
  if (!allowed.includes(next)) {
    throw new Error(
      `非法流转: ${order.status} -> ${next}`
    );
  }
  return { ...order, status: next };
}
把 transitions 声明为 Record<OrderStatus, OrderStatus[]>,如果漏写某个状态,TypeScript 会直接报错,迁移表始终完整。

验证流转与拒绝

const order: Order = { id: "A1001", status: OrderStatus.Pending };

const paid = transition(order, OrderStatus.Paid);
console.log(paid.status); // "PAID"

const shipped = transition(paid, OrderStatus.Shipped);
console.log(shipped.status); // "SHIPPED"

// 非法流转:已发货不能直接取消
transition(shipped, OrderStatus.Cancelled);
// 抛出 Error: 非法流转: SHIPPED -> CANCELLED

常见错误与规避

错误写法问题正确做法
order.status === "paid"大小写与枚举值不一致,永远为 falseorder.status === OrderStatus.Paid
函数参数写 string调用方可传入任意字符串,失去编译检查参数类型写 OrderStatus
迁移表用 Record<string, string[]>漏状态不报错,键名拼错也不报错键和值都用 OrderStatus
枚举成员是运行时对象,Object.values(OrderStatus) 可遍历全部状态值;但反向映射只对数字枚举存在,字符串枚举没有 OrderStatus["PENDING"] 这种反查。