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
相关产品推荐
相关产品推荐

