TypeScript中基于枚举的映射类型如何省略无负载事件定义
解决TypeScript映射类型索引不匹配问题
问题核心
当Payload类型仅包含有payload的事件枚举键时,直接用所有Event枚举值去索引Payload会触发Type 'T' cannot be used to index type 'Payload'错误——因为部分枚举值不在Payload的键集合中。
解决方案
方案1:条件类型+交叉类型
通过条件类型判断当前事件枚举是否存在于Payload的键中,动态生成对应结构:
enum Event { Click, Hover, Submit, } // 仅定义有payload的事件 type Payload = { [Event.Submit]: { formData: FormData }; }; // 映射类型:有payload的事件强制携带对应结构,无payload的事件无需payload字段 type EventWithOptionalPayload<T extends Event> = { type: T; } & (T extends keyof Payload ? { payload: Payload[T] } : { payload?: never }); // 合法用法 const clickEvent: EventWithOptionalPayload<Event.Click> = { type: Event.Click }; const submitEvent: EventWithOptionalPayload<Event.Submit> = { type: Event.Submit, payload: { formData: new FormData() } };
这里payload?: never确保无payload的事件可以完全省略该字段,同时类型检查会阻止错误传入payload。
方案2:拆分事件子集+类型合并
先将Event拆分为有payload和无payload的两个子集,分别定义类型后合并:
enum Event { Click, Hover, Submit, } type Payload = { [Event.Submit]: { formData: FormData }; }; // 拆分事件类型 type EventsWithPayload = keyof Payload; type EventsWithoutPayload = Exclude<Event, EventsWithPayload>; // 分别定义两类事件结构 type EventWithPayload<T extends EventsWithPayload> = { type: T; payload: Payload[T]; }; type EventWithoutPayload<T extends EventsWithoutPayload> = { type: T; }; // 合并为最终映射类型 type EventWithOptionalPayload<T extends Event> = T extends EventsWithPayload ? EventWithPayload<T> : EventWithoutPayload<T>; // 合法用法 const hoverEvent: EventWithOptionalPayload<Event.Hover> = { type: Event.Hover }; const submitEvent: EventWithOptionalPayload<Event.Submit> = { type: Event.Submit, payload: { formData: new FormData() } };
这种方式结构更清晰,无payload的事件类型中完全不存在payload属性,严格符合“省略无payload事件的undefined声明”的需求。
内容的提问来源于stack exchange,提问作者Joel'-'
相关产品推荐
相关产品推荐

