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
相关产品推荐
相关产品推荐

