如何编写TypeScript类型工具RemoveFirstParamOfType移除指定类型首个函数参数
问题描述
我有一个带调用签名的Action接口:
interface Action { ( product: Product, value: Value, cart: Cart, payload?: { [key: string]: any } ): void; } interface Cart { name: string; } interface Product { name: string; } interface Value { id: string; }
我希望将它转换为移除Value参数的UpdateAction版本:
interface UpdateAction { ( product: Product, cart: Cart, payload?: { [key: string]: any } ): void; } const updateProduct: UpdateAction = (product, cart) => { cart.name = product.name; }; const Reducer = ( product: Product, message: Message, cart: Cart, value:Value ) => { const { action, payload } = message; switch (action) { case 'update': updateProduct(product, cart); break; default: { throw new Error('Unknown type'); } } };
但不想手动定义UpdateAction,而是通过类型函数RemoveFirstParamOfType<Action, Value>以编程方式转换,实现从调用签名中移除首个类型为V的参数。
解决方案
可以利用TypeScript的条件类型和元组递归遍历能力,实现这个自动移除指定类型首个参数的类型函数。
类型函数实现
// 核心类型函数:移除函数中首个类型为V的参数 type RemoveFirstParamOfType<F extends (...args: any[]) => any, V> = F extends (...args: infer Args) => infer ReturnType ? Args extends [infer FirstArg, ...infer RestArgs] ? FirstArg extends V ? (...args: RestArgs) => ReturnType : // 不匹配则保留当前参数,继续处理剩余参数 (...args: [FirstArg, ...RemoveFirstParamOfType<(...args: RestArgs) => ReturnType, V>['args']]) => ReturnType : F : F; // 辅助类型:提取函数的参数元组(用于递归时复用) type ExtractArgs<F extends (...args: any[]) => any> = F extends (...args: infer Args) => any ? Args : never;
实际使用
// 自动生成目标类型UpdateAction type UpdateAction = RemoveFirstParamOfType<Action, Value>; // 测试代码,类型完全匹配预期 const updateProduct: UpdateAction = (product, cart, payload) => { cart.name = product.name; }; // 原Reducer函数无需修改,类型校验正常 const Reducer = ( product: Product, message: { action: string; payload?: { [key: string]: any } }, cart: Cart, value: Value ) => { const { action, payload } = message; switch (action) { case 'update': updateProduct(product, cart, payload); break; default: { throw new Error('Unknown type'); } } };
工作原理
- 参数与返回值提取:通过
infer关键字从原函数类型中提取参数元组Args和返回值类型ReturnType。 - 递归遍历参数:从参数元组的第一个元素开始检查,如果该元素的类型匹配目标
V,则直接用剩余参数构造新函数;如果不匹配,就保留当前参数,继续递归处理剩下的参数。 - 边界处理:当参数元组为空时,直接返回原函数类型,避免递归报错。
内容的提问来源于stack exchange,提问作者FD3
相关产品推荐
相关产品推荐

