如何在根模块中为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;
原因
问题核心在于类型信息被过早抹平:
modules: IModule[]强制将所有模块统一为宽泛的IModule类型,丢失了每个模块reducers的具体类型细节;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
相关产品推荐
相关产品推荐

