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

TypeScript:基于if语句的双重类型推断问题

解决游戏事件类型的精准推断与泛型约束问题

第一步:建立事件映射的基础类型

先明确游戏类型、事件名称、事件数据的对应关系,用映射类型把三者绑定:

// 游戏类型枚举
enum EventType {
  QUIZ = 'quiz',
  BATTLE = 'battle'
}

// 核心映射:每个游戏类型对应的事件名称与数据类型
type GameEventMap = {
  [EventType.QUIZ]: {
    start: { quizId: string; questions: string[] },
    answer: { quizId: string; selectedOption: number; isCorrect: boolean },
    finish: { quizId: string; score: number }
  },
  [EventType.BATTLE]: {
    join: { battleId: string; playerId: string },
    attack: { battleId: string; targetId: string; damage: number },
    win: { battleId: string; playerId: string }
  }
}

第二步:修复联合类型的类型收窄问题

原来的联合类型会把同一游戏类型下的所有事件数据合并成联合类型,导致判断evt和evn后无法精准推断evd。我们需要生成每个(evt, evn)组合对应唯一evd类型的联合类型:

// 正确的全事件联合类型
type CorrectAnyEvent = {
  [GameType in keyof GameEventMap]: {
    [EventName in keyof GameEventMap[GameType]]: {
      evt: GameType,
      evn: EventName,
      evd: GameEventMap[GameType][EventName]
    }
  }[keyof GameEventMap[GameType]]
}[keyof GameEventMap]

此时在条件判断中,evd会被精准推断:

function handleEvent(event: CorrectAnyEvent) {
  if (event.evt === EventType.QUIZ && event.evn === 'answer') {
    // evd自动推断为{ quizId: string; selectedOption: number; isCorrect: boolean }
    console.log(event.evd.isCorrect) // 无类型错误
  }
}

第三步:实现支持精准约束的泛型类型

要让AnyEvent<GameType, EventName>能强制限制evn和evd的类型,需要给泛型参数加上约束,确保EventName是对应GameType下的合法事件名称:

// 泛型事件类型:支持指定具体游戏类型和事件名称
type TypedAnyEvent<
  GameType extends keyof GameEventMap,
  EventName extends keyof GameEventMap[GameType] = keyof GameEventMap[GameType]
> = {
  evt: GameType,
  evn: EventName,
  evd: GameEventMap[GameType][EventName]
}

使用示例:

// 仅允许quiz类型的answer事件
const validQuizAnswer: TypedAnyEvent<EventType.QUIZ, 'answer'> = {
  evt: EventType.QUIZ,
  evn: 'answer', // 必须是'answer',否则报错
  evd: { quizId: 'q_001', selectedOption: 3, isCorrect: true } // 必须匹配answer事件的数据结构
}

// 错误示例:evn不符合泛型指定的类型
const invalidEvent: TypedAnyEvent<EventType.QUIZ, 'answer'> = {
  evt: EventType.QUIZ,
  evn: 'start', // TS报错:'"start"'不能赋值给'"answer"'
  evd: { quizId: 'q_001', questions: ['A', 'B'] }
}

核心思路总结

  1. 用GameEventMap建立三层绑定关系,是所有类型推断的基础;
  2. 生成联合类型时,要遍历到每个(evt, evn)组合,避免同一游戏类型下的事件数据被合并;
  3. 泛型通过约束EventName的范围,实现指定参数时的精准类型限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:17:42