TypeScript泛型优化:实现trackEvent参数按需必填
解决方案
可以通过泛型条件类型结合剩余参数实现需求,让第二个参数在事件对应值为undefined时可选,其他情况必填。
实现代码
export type Events = { LOGIN: undefined NAVIGATION: { screen: string } SUPPORT: { communication_method: 'chat' | 'email' | 'phone' } } export function trackEvent<K extends keyof Events>( eventName: K, ...args: Events[K] extends undefined ? [] : [eventValues: Events[K]] ) { const eventValues = args[0] as Events[K]; if (Platform.OS === 'web') return; logEvent(eventName, eventValues ?? {}); } // 测试用例 trackEvent('LOGIN') // ✅ 无TS错误 trackEvent('LOGIN', undefined) // ✅ 仍支持传undefined trackEvent('SUPPORT') // ❌ TS错误:缺少第二个参数 trackEvent('SUPPORT', { communication_method: 'chat' }) // ✅ 正常提示 trackEvent('NAVIGATION') // ❌ TS错误:缺少第二个参数 trackEvent('NAVIGATION', { screen: 'home' }) // ✅ 正常提示
原理说明
- 利用剩余参数
...args结合条件类型:Events[K] extends undefined ? [] : [eventValues: Events[K]] - 当事件
K对应的Events[K]是undefined时,要求剩余参数为空数组,即无需传递第二个参数 - 当
Events[K]是其他类型时,要求剩余参数必须包含一个对应类型的元素,即第二个参数必填 - 函数内部通过
args[0]获取参数值,并用类型断言确保类型匹配
替代方案:函数重载
如果需要更直观的签名提示,可以用函数重载实现,但这种方式需要手动维护重载列表,新增值为undefined的事件时需要更新重载:
// 重载1:必填参数的事件 export function trackEvent<K extends Exclude<keyof Events, 'LOGIN'>>(eventName: K, eventValues: Events[K]): void; // 重载2:可选参数的事件 export function trackEvent(eventName: 'LOGIN', eventValues?: undefined): void; // 实现函数 export function trackEvent<K extends keyof Events>(eventName: K, eventValues?: Events[K]) { if (Platform.OS === 'web') return; logEvent(eventName, eventValues ?? {}); }
内容的提问来源于stack exchange,提问作者Norfeldt
相关产品推荐
相关产品推荐

