使用TypeScript泛型作为索引:事件监听器类型定义疑问
事件监听器泛型类型的类型检查问题
你写的这段代码思路是对的——你预期connected事件的回调会触发类型错误完全合理,因为connected对应的回调参数被定义为number类型,而number根本没有stack属性,访问err.stack属于明显的类型不匹配。
先确认问题核心
在你的代码里:
type Name = "error" | "connected"; type Callback = { error: (err: Error) => void, connected: (err: number) => void, }; function on<T extends Name>(eventName: T, callback: Callback[T]): void { } // 这里err是Error类型,访问stack没问题 on("error", (err) => err.stack); // 这里err应该是number类型,访问stack理应报错 on("connected", (err) => err.stack);
正常情况下,TypeScript会给第二个on调用抛出类型错误:Property 'stack' does not exist on type 'number'。如果你的环境没触发这个错误,大概率是TypeScript的配置不够严格——检查下tsconfig.json里是否开启了strict: true(至少要开启noImplicitAny),严格模式下类型推断才会精准生效。
优化你的类型定义
虽然初始代码的泛型思路可行,但可以用更清晰的方式定义事件与回调的映射,让类型检查更直观:
// 用元组统一定义每个事件的回调参数列表 type EventMap = { error: [Error], connected: [number] }; // 直接基于EventMap的键做泛型约束 function on<K extends keyof EventMap>( eventName: K, callback: (...args: EventMap[K]) => void ): void {} // 合法调用:err是Error类型,stack属性存在 on("error", (err) => console.log(err.stack)); // 类型错误:number类型没有stack属性(TypeScript会直接标出) on("connected", (err) => console.log(err.stack));
这种方式的优势在于,EventMap可以很方便地扩展新事件,而且回调的参数类型会被TypeScript自动精准推断,不会出现模糊的情况。
总结
你的初始代码逻辑没问题,类型错误没触发的话优先检查TS配置;如果想让类型系统更健壮,推荐用EventMap+元组的方式来定义事件回调的类型映射。
内容的提问来源于stack exchange,提问作者ed'
相关产品推荐
相关产品推荐

