如何利用泛型实现logEvent函数的第二个参数可选
如何利用泛型实现logEvent函数的第二个参数可选
这个需求其实挺常见的,我来给你捋捋怎么实现~
首先咱们先明确问题:你当前的logEvent函数通过泛型关联了事件名和对应的参数类型,但不管参数类型是不是undefined,第二个参数都得传,哪怕传undefined本身。现在想让对应类型为undefined的事件(比如event1)可以省略第二个参数,其他事件(比如event2)依然强制要求传参。
方案一:泛型条件+剩余参数(灵活扩展版)
这种方式最适合你的场景,而且后续如果EventType新增事件类型,不需要修改函数定义,TypeScript会自动推导:
type EventType = { event1: undefined, event2: { user: number } } type EventName = keyof EventType; function logEvent<T extends EventName>( eventName: T, ...args: EventType[T] extends undefined ? [] | [undefined] : [EventType[T]] ) { const extraData = args[0]; console.log(`Logged event: ${eventName}`, extraData); } // 测试调用: logEvent('event1'); // ✅ 正常,第二个参数可选 logEvent('event1', undefined); // ✅ 也允许传undefined logEvent('event2', { user: 5 }); // ✅ 正常 logEvent('event2'); // ❌ 报错,必须传第二个参数 logEvent('event2', { user: '5' }); // ❌ 报错,参数类型不匹配
原理是利用条件类型判断当前事件对应的参数类型是否为undefined:
- 如果是
undefined,允许剩余参数为空数组(即省略第二个参数)或者传一个undefined; - 如果不是
undefined,则要求剩余参数必须是包含对应类型的数组(即必须传第二个参数)。
方案二:函数重载(直观固定版)
如果你的EventType后续不会频繁新增事件,用函数重载会更直观,代码可读性更高:
type EventType = { event1: undefined, event2: { user: number } } type EventName = keyof EventType; // 重载签名:针对每个事件定义参数规则 function logEvent(eventName: 'event1', extraData?: undefined): void; function logEvent(eventName: 'event2', extraData: { user: number }): void; // 实现签名:兼容所有重载的通用逻辑 function logEvent<T extends EventName>(eventName: T, extraData?: EventType[T]) { console.log(`Logged event: ${eventName}`, extraData); } // 测试调用效果和方案一完全一致 logEvent('event1'); // ✅ logEvent('event2', { user: 5 }); // ✅ logEvent('event2'); // ❌ 报错
这种方式需要为每个事件单独写重载签名,好处是规则一目了然,适合事件类型固定的场景。
备注:内容来源于stack exchange,提问作者Evert
相关产品推荐
相关产品推荐

