TypeScript:如何根据函数值的回调参数特征筛选对象键名
解决TypeScript筛选符合回调要求的事件键名问题
我来帮你搞定这个类型定义的问题!核心是要精准约束最后一个参数的类型,同时先确保我们处理的对象值都是函数类型,避免你遇到的约束错误。
完整解决方案代码
// 辅助类型:获取数组的最后一个元素 type Last<T extends any[]> = T extends [...infer _, infer L] ? L : never; // 辅助类型:判断一个函数是否是「接受单个任意参数且返回void」的回调 type IsValidCallback<F> = F extends (arg: any) => void ? true : false; // 核心类型:筛选出符合要求的事件键名 type EventsWithCallback<T extends Record<string, (...args: any[]) => any>> = keyof { [P in keyof T as IsValidCallback<Last<Parameters<T[P]>>> extends true ? P : never ]: T[P] }; // 你的示例测试 interface Events { 'valid0': (data: string, cb: () => void) => void, // 符合要求 'valid1': (data: number, cb: (data: string) => void) => void, // 符合要求 'invalid0': (data: string, cb: () => string) => void, // 不符合要求:回调返回类型错误 'invalid1': (data: string, cb: (string: string, number: number) => void) => void, // 不符合要求:回调参数数量错误 } type testFunction<E> = (eventName: EventsWithCallback<E>) => void; // 测试:应该只允许传入'valid0' | 'valid1' const test: testFunction<Events> = (eventName) => { console.log(eventName); }; test('valid0'); // ✅ 正常 test('valid1'); // ✅ 正常 // test('invalid0'); // ❌ 类型错误 // test('invalid1'); // ❌ 类型错误
关键细节解释
约束输入类型T:
我们给EventsWithCallback加上了T extends Record<string, (...args: any[]) => any>的约束,确保T的所有键对应的值都是函数,这样Parameters<T[P]>就不会出现你之前遇到的Type 'T[P]' does not satisfy the constraint '(...args: any[]) => void'错误。精准判断回调类型:
IsValidCallback类型专门用来校验回调:- 要求回调是一个函数
- 接受恰好一个任意类型的参数(对应你示例里的
valid1情况) - 返回值必须是
void(排除invalid0这种返回string的情况)
过滤符合要求的键:
利用TypeScript的映射类型as子句,遍历T的每个键P:- 先取该键对应函数的参数列表
Parameters<T[P]> - 获取最后一个参数
Last<...> - 用
IsValidCallback判断是否符合要求,符合则保留键P,否则设为never(会被自动过滤)
- 先取该键对应函数的参数列表
为什么你之前的代码有问题?
- 没有给T加上函数对象的约束,导致
Parameters<T[P]>可能接收到非函数类型,触发错误 extends Function过于宽泛,会匹配所有函数类型,无法精准限制参数数量和返回值类型
内容的提问来源于stack exchange,提问作者panwauu
相关产品推荐
相关产品推荐

