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

TypeScript中如何将函数对象转换为保留原签名的带类型新函数对象

实现带类型推导的Redux Action包装函数

单个Action的包装函数(useDispatchAction)

完全可以实现自动提取原action函数参数类型的单个包装函数,利用TypeScript泛型即可完成:

import { useDispatch } from 'react-redux';

const useDispatchAction = <T extends (...args: any[]) => any>(actionCreator: T) => {
  const dispatch = useDispatch();
  return (...args: Parameters<T>): ReturnType<typeof dispatch> => {
    return dispatch(actionCreator(...args));
  };
};

// 使用示例
const doSomethingAction = (id: number, name: string) => ({
  type: 'DO_SOMETHING',
  payload: { id, name }
});

const doSomething = useDispatchAction(doSomethingAction);
doSomething(123, 'test'); // 类型检查通过
doSomething('123', 'test'); // 类型检查报错(参数类型不匹配)
doSomething(123); // 类型检查报错(缺少参数)

这个泛型函数会自动捕获原actionCreator的参数类型,约束返回函数的参数必须完全匹配,同时保证返回值类型与dispatch一致。

批量Action的包装函数(useActions)

针对多action场景,结合映射类型和条件类型,既能简化代码,也能保留完整的类型推导,还支持自动去除Action后缀:

import { useDispatch } from 'react-redux';

type ActionCreators = Record<string, (...args: any[]) => any>;

type WrappedActions<T extends ActionCreators> = {
  [K in keyof T as K extends `${infer Name}Action` ? Name : K]: 
    (...args: Parameters<T[K]>) => ReturnType<typeof useDispatch>;
};

const useActions = <T extends ActionCreators>(actionObj: T): WrappedActions<T> => {
  const dispatch = useDispatch();
  return Object.entries(actionObj).reduce((acc, [key, actionCreator]) => {
    const wrappedKey = key.replace(/Action$/, '') as keyof WrappedActions<T>;
    acc[wrappedKey] = (...args: any[]) => dispatch(actionCreator(...args)) as any;
    return acc;
  }, {} as WrappedActions<T>);
};

// 使用示例
const actions = {
  doSomethingAction: (id: number, name: string) => ({
    type: 'DO_SOMETHING',
    payload: { id, name }
  }),
  fetchDataAction: (url: string) => ({
    type: 'FETCH_DATA',
    payload: url
  })
};

const { doSomething, fetchData } = useActions(actions);
doSomething(123, 'test'); // 类型检查通过
fetchData('/api/data'); // 类型检查通过
doSomething('123'); // 类型检查报错

WrappedActions类型通过映射类型遍历原action对象的键,用条件类型自动去除Action后缀,同时为每个属性生成参数匹配的包装函数。

剥离React上下文的纯包装函数

如果只需要纯类型包装逻辑,不依赖React hooks,可以这样实现:

单个函数包装

type WrapperFunc<T extends (...args: any[]) => any> = 
  (...args: Parameters<T>) => ReturnType<T>;

const wrapperTypeOne = <T extends (...args: any[]) => any>(func: T): WrapperFunc<T> => {
  return (...args) => func(...args);
};

// 使用示例
const exampleFunc = (first: string, second: number) => `${first} #${second}`;
const wrapped = wrapperTypeOne(exampleFunc);
wrapped('hello', 2); // 正常运行
wrapped('hello'); // 类型检查失败
wrapped(2, 2); // 类型检查失败

批量对象包装

type WrappedObject<T extends Record<string, (...args: any[]) => any>> = {
  [K in keyof T]: (...args: Parameters<T[K]>) => ReturnType<T[K]>;
};

const wrapperTypeTwo = <T extends Record<string, (...args: any[]) => any>>(obj: T): WrappedObject<T> => {
  const result = {} as WrappedObject<T>;
  for (const key in obj) {
    result[key] = (...args) => obj[key](...args);
  }
  return result;
};

// 使用示例
const wrapped2 = wrapperTypeTwo({ exampleFunc });
wrapped2.exampleFunc('hello', 2); // 正常运行
wrapped2.exampleFunc('hello'); // 类型检查失败
wrapped2.exampleFunc(2, 2); // 类型检查失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:15:41