TypeScript类浏览器事件系统never类型报错的简洁修复方案
问题描述
我正在实现一个类浏览器的事件系统,代码定义如下:
import {Point2D} from "../src/framework/models/level"; export type EventType = keyof EventTypeMap export type EventTypeMap = { click: ClickEvent hover: HoverEvent } export type CEvent = ClickEvent | HoverEvent export type EventListener<T extends CEvent> = (event: T) => void export type EventListenerMap = {[key in EventType]: EventListener<EventTypeMap[key]>} export type HoverEvent = { type: "hover" position: Point2D } export type ClickEvent = { type: "click" position: Point2D } const listeners: EventListenerMap = { click: (event) => {}, hover: (event) => {} } const onEvent = (event: CEvent) => { listeners[event.type](event); } onEvent({type: "click", position: {x: 0, y: 0}})
这是简化后的示例,onEvent函数出现了TypeScript错误:
error TS2345: Argument of type 'CEvent' is not assignable to parameter of type 'never'. The intersection 'ClickEvent & HoverEvent' was reduced to 'never' because property 'type' has conflicting types in some constituents. Type 'ClickEvent' is not assignable to type 'never'.
添加类型守卫后可以正常运行,但这种switch写法冗余(我不需要对不同事件类型做特殊处理):
const onEvent = (event: CEvent) => { switch (event.type) { case "click": listeners[event.type](event); break; case "hover": listeners[event.type](event); break; } }
请问有没有办法在保持函数简洁的前提下修复该错误?
解决方案
可以通过以下几种简洁方式修复类型错误:
方案1:泛型约束实现精确类型推导
将onEvent定义为泛型函数,让TypeScript自动关联事件类型与监听函数的参数类型,同时还能严格约束入参:
const onEvent = <T extends keyof EventTypeMap>(event: EventTypeMap[T]) => { listeners[T](event); };
方案2:类型断言快速兼容
如果不想修改函数结构,直接用类型断言告诉TypeScript当前事件与监听函数的匹配关系:
const onEvent = (event: CEvent) => { (listeners[event.type] as EventListener<CEvent>)(event); };
方案3:简化类型守卫写法
如果偏好分支判断但不想用switch,可以用更简洁的if-else写法,比switch少冗余代码:
const onEvent = (event: CEvent) => { event.type === "click" ? listeners.click(event) : listeners.hover(event); };
其中泛型方案是最优解,既保持函数简洁,又能维持严格的类型检查,避免后续扩展事件类型时出现类型漏洞。
内容的提问来源于stack exchange,提问作者Symtox
相关产品推荐
相关产品推荐

