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

如何在根模块中为RTK Reducer保留类型以支持useAppSelector?

问题

我采用模块化架构开发React应用,每个模块通过统一的IModule接口定义:

const authModule: IModule = {
  routes,
  reducers: {},
};

export type IModule = {
  routes?: IRoute[]
  reducers: IReducers
};

随后在根模块汇总所有模块:

const modules: IModule[] = [
  authModule,
  userModule,
  weatherForecastModule,
  commonModule,
];

const routes = modules.reduce(
  (acc: IRoute[], module) => (!module.routes ? acc : [...acc, ...module.routes]),
  [],
);
const reducers = modules.reduce(
  (acc: IReducers, module) => ({ ...acc, ...module.reducers }),
  {},
);

const rootModule: IModule = {
  routes: routes,
  reducers,
};

export default rootModule;

接着将所有reducers添加到Store中:

const store = configureStore({
 reducer: {
   [baseApi.reducerPath]: baseApi.reducer,
   ...rootModule.reducers,
   userReducer,
   screenReducer,
 },
 middleware: (getDefaultMiddleware) => getDefaultMiddleware()
   .concat(baseApi.middleware),
 devTools: true,
});

setupListeners(store.dispatch);

export default store;

但合并reducers的过程中丢失了类型信息,导致useAppSelector无法提示可用的reducers:

const user = useAppSelector((state) => state.???);

补充类型定义:

export type RootState = ReturnType<typeof store.getState>;
export type AppStore = typeof store;
export type AppDispatch = AppStore['dispatch'];

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

原因

问题核心在于类型信息被过早抹平:

  1. modules: IModule[] 强制将所有模块统一为宽泛的IModule类型,丢失了每个模块reducers的具体类型细节;
  2. reduce函数中硬编码acc: IReducers,进一步限制了累加结果的类型范围,让TypeScript无法推导合并后的具体reducer结构。

修复方案

1. 让IModule支持泛型,保留模块reducers的具体类型

修改IModule定义,用泛型参数存储当前模块reducers的专属类型:

export type IModule<TReducers extends IReducers = IReducers> = {
  routes?: IRoute[];
  reducers: TReducers;
};

2. 取消模块数组的显式类型声明

不要给modules加IModule[]的约束,让TypeScript自动推导每个模块的具体泛型类型:

// 移除: IModule[],保留自动推导的联合类型
const modules = [
  authModule,
  userModule,
  weatherForecastModule,
  commonModule,
];

3. 优化reducer合并的类型推导逻辑

去掉reduce中累加器的显式类型约束,仅在初始化时指定基础类型,让TS自动合并每个模块的reducers类型:

const reducers = modules.reduce(
  (acc, module) => ({ ...acc, ...module.reducers }),
  {} as IReducers, // 仅初始化基础结构,后续由TS自动扩展具体类型
);

4. 根模块保留自动推导的类型

不要给rootModule加IModule的显式类型,让TS自动保留其包含的具体reducers结构:

// 移除: IModule,保留自动推导的完整类型
const rootModule = {
  routes: routes,
  reducers,
};

5. 验证类型提示

此时RootState会自动包含所有模块reducers的具体类型,useAppSelector就能正常提示state下的所有reducer键名:

// 现在会有明确的类型提示,比如state.userReducer、state.screenReducer等
const user = useAppSelector((state) => state.userReducer);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:14:53