TS中createEntityAdapter调用getSelectors报类型错误如何修复
问题描述
开发TypeScript练手项目时,在apiSlice.ts中添加entityAdapter选择器代码后触发TS类型校验报错,触发报错的代码段如下:
export const { selectAll: selectAllPizzas } = pizzasAdapter.getSelectors( state => selectPizzasData(state) ?? initialState )
报错分三个阶段出现:
- 初始报错:
state参数被推断为unknown类型,无法匹配Redux要求的RootState类型 - 从
store.ts导入按Redux Toolkit官方规范生成的RootState类型给state加类型标注后,触发新报错:回调返回值类型为EntityState<unknown> | PizzaResponseType[],不符合getSelectors要求的EntityState类型,PizzaResponseType[]数组缺少EntityState必需的ids、entities属性 - 调整配置:尝试将
getPizzas查询的返回类型定义为EntityState<PizzaResponseType>,在transformResponse中返回pizzasAdapter.setAll处理后的标准化状态后,再次触发报错:EntityState<unknown>无法赋值给EntityState<IPizzaType>类型,unknown类型无法匹配IPizzaType的类型约束
修复步骤
所有报错的核心原因是createEntityAdapter、RTK Query endpoint、selector三个环节的类型没有显式声明、全链路不统一,按以下步骤调整即可解决:
- 初始化实体适配器时显式传入泛型类型,禁止依赖隐式推断
// 提前统一实体类型,保证接口返回的单条披萨数据结构和该类型完全一致,不要混用IPizzaType、PizzaResponseType两种不兼容的类型 type PizzaEntity = IPizzaType; // 错误写法:不传泛型时,TS会把适配器的实体类型默认推断为unknown // const pizzasAdapter = createEntityAdapter(); // 正确写法 const pizzasAdapter = createEntityAdapter<PizzaEntity>({ // 若数据主键不是默认的id字段,在此处显式指定 selectId: (item) => item.pizzaId, // 可选:配置列表排序规则 sortComparer: (a, b) => a.price - b.price });
- 配置RTK Query的endpoint时,显式标注查询返回值类型、transformResponse入参类型
// apiSlice中getPizzas接口配置 endpoints: (builder) => ({ getPizzas: builder.query<EntityState<PizzaEntity>, void>({ query: () => '/api/pizzas', // 标注原始响应的类型为接口返回的数组类型 transformResponse: (rawResponse: PizzaEntity[]) => { // 用适配器的setAll方法生成标准化EntityState,传入适配器生成的初始空状态作为第一个参数 return pizzasAdapter.setAll(pizzasAdapter.getInitialState(), rawResponse); } }) })
- 生成选择器时,统一状态类型,不要用普通数组作为兜底初始值
// 从endpoint导出对应接口的数据选择器 const selectPizzasEntry = apiSlice.endpoints.getPizzas.select(); // 兜底初始值必须用适配器提供的getInitialState生成,保证类型为EntityState<PizzaEntity> const pizzasInitialState = pizzasAdapter.getInitialState(); export const { selectAll: selectAllPizzas } = pizzasAdapter.getSelectors( (state: RootState) => selectPizzasEntry(state)?.data ?? pizzasInitialState );
注意:如果
PizzaResponseType和IPizzaType字段存在差异,需要在transformResponse阶段先做字段映射对齐,不要直接把结构不匹配的数据传入适配器方法,否则依然会触发类型报错。
内容的提问来源于stack exchange,提问作者Alex Kor
相关产品推荐
相关产品推荐

