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

Redux Toolkit中实现选择器全局化及正确类型定义

问题

在Redux Toolkit的slice文件中,我创建了如下局部选择器:

const getSomething = (state: TreeState) => simpleOperations(state.something)

该选择器返回number类型,我会在reducer中复用它以避免代码重复。现在我希望将这些选择器导出到组件中使用,目前的写法是:

const something = useSelector(state => getSomething(state.tree))

(假设全局状态RootState包含类型为TreeState的子状态tree)

我希望简化为:

const something = useSelector(getSomething)

因此我计划将getSomething设为slice内私有,通过工具函数globalizeSelectors将所有局部选择器包装为全局选择器并导出。我期望局部选择器(state: TreeState) => number能被包装为签名为(state: RootState) => number的全局选择器,且返回类型一致。

我当前的实现如下:

const getSomething = (state: TreeState) => simpleOperations(state.something)

function globalizeSelectors<T extends {}>(subStateSelector: (state: RootState) => T, selectors: { [key: string]: (subState: T) => any }) {
    const globalized: { [key: keyof typeof selectors]: (state: RootState) => any } = {}
    for (let selector in selectors)
        globalized[selector] = (state: RootState) => selectors[selector](subStateSelector(state))

    return globalized
}

export const treeSelectors = globalizeSelectors<TreeState>(state => state.tree, {getSomething})

组件中的使用示例:

const something = useSelector(treeSelectors.getSomething)

我需要TypeScript能正确推断:

  • treeSelectors包含getSomething属性
  • getSomething的签名为(state: RootState) => number
  • something为number类型
  • 支持任意数量、任意返回类型的局部选择器

但当前实现无法让TypeScript正确推断类型,请问该如何修改?globalizeSelectors可完全重写。

解决方案

要让TypeScript正确推断所有类型,需要细化globalizeSelectors的泛型定义,捕获每个局部选择器的精确类型信息,而非用any模糊处理。

修改后的工具函数实现如下:

// 泛型S:局部状态类型;泛型T:传入的选择器对象的精确类型
function globalizeSelectors<S, T extends Record<string, (state: S) => unknown>>(
  subStateSelector: (state: RootState) => S,
  selectors: T
): {
  [K in keyof T]: (state: RootState) => ReturnType<T[K]>
} {
  const globalized: Record<string, unknown> = {}
  for (const key in selectors) {
    if (Object.prototype.hasOwnProperty.call(selectors, key)) {
      globalized[key] = (state: RootState) => selectors[key](subStateSelector(state))
    }
  }
  return globalized as { [K in keyof T]: (state: RootState) => ReturnType<T[K]> }
}

在slice中使用的示例:

// 局部选择器保持私有
const getSomething = (state: TreeState) => simpleOperations(state.something)
const getAnotherThing = (state: TreeState) => state.anotherValue // 示例:返回string类型

// 导出全局选择器
export const treeSelectors = globalizeSelectors(state => state.tree, {
  getSomething,
  getAnotherThing
})

关键修改说明

  1. 精确泛型捕获:用泛型T捕获传入的选择器对象的完整类型,包括每个选择器的键名和返回值类型,避免索引类型的模糊性。
  2. 返回类型映射:通过{ [K in keyof T]: (state: RootState) => ReturnType<T[K]> },将每个局部选择器的类型映射为接收全局RootState、返回相同结果类型的全局选择器。
  3. 安全类型断言:遍历构建后的对象结构完全符合映射后的类型,因此用类型断言完成转换,避免不必要的类型错误。

组件中的类型推断效果

// TypeScript自动推断something为number类型
const something = useSelector(treeSelectors.getSomething)
// TypeScript自动推断anotherThing为string类型
const anotherThing = useSelector(treeSelectors.getAnotherThing)

该实现完全满足需求:自动识别选择器属性、正确的函数签名、精确的返回类型,同时支持任意数量和返回类型的局部选择器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 06:25:28