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

TypeScript函数参数类型推断:游戏事件类型约束需求

实现游戏事件的类型联动与参数收窄

需求说明

我定义了国际象棋事件(Chess Event)与问答事件(Quiz Event)两类游戏事件类型结构,希望TypeScript和VSCode的IntelliSense能通过send函数的参数实现类型收窄,达成以下效果:

  • 传入EventType.CHESS时,仅提示国际象棋相关的事件名,且事件数据需匹配对应类型
  • 传入EventType.QUIZ时,仅提示问答相关的事件名,且事件数据需匹配对应类型
  • 错误的事件类型与事件名组合会触发TypeScript错误

示例预期行为:

(() => {
    // 合法调用
    send(EventType.CHESS, "goback", {gameId: "1", count: 5});
    send(EventType.QUIZ, "answer", {gameId: "2", body: "my answer is..."}, {timeout: 1000});
    // 非法调用,应报错:"make_move"属于国际象棋事件,不能搭配EventType.QUIZ
    send(EventType.QUIZ, "make_move", {gameId: "3", from: "2D", to: "4E"}, {timeout: 1000});

    // 输入send(EventType.QUIZ, 时,IntelliSense应仅提示QUIZ相关事件名
})();

现有类型定义与函数代码

type EventAny<TYPE extends EventType, EventList extends Record<string, any>> = { [K in keyof EventList]:
    { evt: TYPE, evn: K, evd: EventList[K] }
}[keyof EventList];

type EventChess = EventAny<EventType.CHESS, ChessEventList>;
type EventQuiz = EventAny<EventType.QUIZ, QuizEventList>;

type NetEvent = (EventChess | EventQuiz);

enum EventType {
    CHESS   = 0,
    QUIZ    = 1,
}

interface EventData { // 公共参数
    gameId: string;
}

interface ChessEventList {
    "make_move": EventData & {
        from: string;
        to: string;
    }

    "goback": EventData & {
        count: number;
    }
}

interface QuizEventList {
    "answer": EventData & {
        body: string;
    }

    "rate_current": EventData & {
        rate: number;
    }
}

interface SomeSendOptions {
    timeout?: number;
}

// 现有send函数,无法实现类型联动
function send(evt: NetEvent["evt"], evn: NetEvent["evn"], evd: NetEvent["evd"], options?: SomeSendOptions) {
    // 函数逻辑
}

解决方案

要实现类型联动,需将send函数改为泛型函数,通过泛型参数关联事件类型、事件名和事件数据的对应关系:

// 新增映射类型,关联EventType与对应的事件列表
type EventTypeToEventList = {
    [EventType.CHESS]: ChessEventList;
    [EventType.QUIZ]: QuizEventList;
};

// 重新定义send函数
function send<T extends EventType>(
    evt: T,
    evn: keyof EventTypeToEventList[T],
    evd: EventTypeToEventList[T][typeof evn],
    options?: SomeSendOptions
) {
    // 函数逻辑保持不变
}

原理说明

  1. EventTypeToEventList建立了EventType枚举值与对应事件列表的映射,明确EventType.CHESS对应ChessEventList,EventType.QUIZ对应QuizEventList
  2. 泛型参数T捕获传入的evt类型,后续参数通过EventTypeToEventList[T]获取当前事件类型对应的事件列表
  3. evn被约束为当前事件列表的合法键(即事件名),evd则被约束为该事件名对应的具体数据类型
  4. 这样当输入send(EventType.CHESS, ...)时,IntelliSense会自动提示ChessEventList中的事件名,且事件数据必须匹配对应类型;错误组合会直接触发TypeScript错误

验证效果

修改后,示例中的非法调用会触发错误,且IntelliSense会根据传入的evt参数自动过滤合法的事件名,完全符合预期需求。

内容的提问来源于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 16:37:53