如何在TypeScript中无需复制泛型参数改造通用函数?
实现带副作用的createSlice高阶封装(无需复制泛型参数)
核心思路是用高阶函数包装原createSlice,借助TypeScript的泛型推断能力直接复用原函数的所有泛型定义,完全避免手动复制参数类型的繁琐操作,还能解决原函数内部类型未导出的问题。
步骤1:获取原createSlice的类型签名
不管原createSlice的泛型有多复杂,我们不需要手动写出来,直接通过typeof提取它的类型:
// 直接获取原函数的类型,无需关心内部泛型细节 type BaseCreateSlice = typeof createSlice;
步骤2:定义副作用的类型约定
根据需求定义副作用的结构,这里以接收slice名称和状态为例:
// 副作用函数的类型,自动关联slice的状态类型 type SliceSideEffects<State> = Record<string, (sliceName: string, state: State) => void | Promise<void>>;
步骤3:实现高阶封装函数
通过泛型约束和类型推断,让包装函数自动继承原createSlice的所有泛型参数:
function createSliceWithSideEffects(baseCreateSlice: BaseCreateSlice) { return <Args extends Parameters<BaseCreateSlice>>( sliceConfig: Args[0] & { // 自动推断原slice的初始状态类型 sideEffects?: SliceSideEffects<ReturnType<Args[0]['getInitialState']>> } ) => { // 调用原函数创建基础slice const baseSlice = baseCreateSlice(sliceConfig as Args[0]); const initialState = baseSlice.getInitialState(); // 添加副作用执行方法 const runSideEffects = async () => { if (!sliceConfig.sideEffects) return; for (const effect of Object.values(sliceConfig.sideEffects)) { await effect(baseSlice.name, initialState); } }; // 返回合并了副作用能力的slice实例 return { ...baseSlice, runSideEffects }; }; }
使用示例
用包装后的函数创建slice,完全继承原createSlice的类型提示,同时支持副作用配置:
// 包装原createSlice得到带副作用的版本 const enhancedCreateSlice = createSliceWithSideEffects(createSlice); // 创建带副作用的counter slice,类型自动匹配 const counterSlice = enhancedCreateSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: (state) => { state.value++ }, }, sideEffects: { logInit: (name, state) => { console.log(`[${name}] 初始值: ${state.value}`); }, fetchRemoteData: async (name) => { // 模拟异步副作用操作 const res = await fetch(`/api/${name}/init-data`); const data = await res.json(); // 可结合dispatch更新状态,示例省略 } } }); // 执行所有副作用 counterSlice.runSideEffects();
方案优势
- 零重复泛型:完全依赖TypeScript的类型推断,不需要手动复制原createSlice的任何泛型参数
- 兼容未导出类型:即使原createSlice的内部类型(比如Slice、State)没导出,也能通过
Parameters和ReturnType自动推断出来 - 低维护成本:原createSlice的泛型或参数更新时,封装函数会自动同步,不需要手动修改
内容的提问来源于stack exchange,提问作者Vlad Furman
相关产品推荐
相关产品推荐

