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

TypeScript柯里化createSelector类型约束失效,如何修复?

解决柯里化createSelector的TypeScript类型约束问题

问题原因

当使用柯里化版本的createSelector时,TypeScript无法从最终赋值的目标类型(Selector<ExampleState>)反向推断第一层函数的泛型S。因为第一层调用createSelector(...)返回的是一个待调用的函数,而非直接返回Selector<S,R>,此时TypeScript的上下文类型推断无法跨越两层函数传递S的约束,导致S被默认推断为unknown。

而直接返回Selector的版本中,TypeScript可以直接从赋值目标推断出S的类型,因为函数的返回值类型直接与目标类型关联。

修复方案

我们可以调整createSelector的泛型定义,优化类型推断逻辑,让TypeScript能准确推导状态类型S:

优化后的函数签名

type Selector<S = any, R = unknown> = (state: S) => R

interface DEFINED_SELECTORS {
    selectFoo: number
    selectBar: string
}

type SELECTOR_NAMES = keyof DEFINED_SELECTORS

type SelectorResult<T> = T extends SELECTOR_NAMES ? DEFINED_SELECTORS[T] : never

type Dependency<S = any> = SELECTOR_NAMES | Selector<S>

// 提取依赖的状态类型,取所有依赖状态的交集作为通用状态类型
type DepState<T> = T extends SELECTOR_NAMES ? any : T extends Selector<infer S> ? S : never
type AllDepStates<T extends any[]> = { [I in keyof T]: DepState<T[I]> }
type CommonState<T extends any[]> = AllDepStates<T>[number] extends infer U 
  ? U extends any ? (x: U) => void extends (x: AllDepStates<T>[number]) => void ? U : never : never 
  : never

function createSelector<T extends Array<Dependency>>(
  ...args: T
): <R>(
  callback: (
    ...args: {
      [I in keyof T]: T[I] extends SELECTOR_NAMES
        ? SelectorResult<T[I]>
        : ReturnType<Exclude<T[I], SELECTOR_NAMES>>
    }
  ) => R
) => Selector<CommonState<T>, R>
function createSelector(...args: any[]) {
  // 实际实现逻辑
  return (callback: any) => (state: any) => {
    const dependencies = args.map((dep) => 
      typeof dep === 'string' ? state[dep.slice(6)] : dep(state)
    )
    return callback(...dependencies)
  }
}

使用验证

调整后在SelectorInitializer中使用时,S会被正确约束为ExampleState,依赖中的函数参数也能获取到正确的状态类型:

type ExampleState = {
    foo: number
    bar: string
}

type SelectorInitializer = (cfg: {
    state: ExampleState
    selectors: Record<string, Selector<ExampleState>>
}) => void 

declare const init: SelectorInitializer

init({
    state: { foo: 123, bar: 'stringy' },
    selectors: {
        selectFoo: state => state.foo,
        selectBar: state => state.bar,
        selectFizz: createSelector('selectBar', "selectFoo")((bar, foo) => "fizz"),
        selectBuzz: createSelector("selectFoo", state => state.bar, state => 123)((foo, bar, num) => num),
    }
})

关键优化点

  • 调整泛型推断逻辑:通过infer提取依赖中Selector函数的状态类型,取所有依赖状态的交集得到通用状态类型,确保所有依赖都能兼容该状态。
  • 延迟状态类型绑定:让状态类型S的推断与最终返回的Selector类型直接关联,TypeScript可以通过上下文赋值目标反向推导S的具体类型。
  • 简化类型映射:优化依赖参数到回调参数的类型映射逻辑,让类型推断链路更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 11:47:05