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 ) { // 函数逻辑保持不变 }
原理说明
EventTypeToEventList建立了EventType枚举值与对应事件列表的映射,明确EventType.CHESS对应ChessEventList,EventType.QUIZ对应QuizEventList- 泛型参数
T捕获传入的evt类型,后续参数通过EventTypeToEventList[T]获取当前事件类型对应的事件列表 evn被约束为当前事件列表的合法键(即事件名),evd则被约束为该事件名对应的具体数据类型- 这样当输入
send(EventType.CHESS, ...)时,IntelliSense会自动提示ChessEventList中的事件名,且事件数据必须匹配对应类型;错误组合会直接触发TypeScript错误
验证效果
修改后,示例中的非法调用会触发错误,且IntelliSense会根据传入的evt参数自动过滤合法的事件名,完全符合预期需求。
内容的提问来源于stack exchange,提问作者Sonaht
相关产品推荐
相关产品推荐

