You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写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');
        }
    }
};

工作原理

  1. 参数与返回值提取:通过infer关键字从原函数类型中提取参数元组Args和返回值类型ReturnType。
  2. 递归遍历参数:从参数元组的第一个元素开始检查,如果该元素的类型匹配目标V,则直接用剩余参数构造新函数;如果不匹配,就保留当前参数,继续递归处理剩下的参数。
  3. 边界处理:当参数元组为空时,直接返回原函数类型,避免递归报错。

内容的提问来源于stack exchange,提问作者FD3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 05:10:37