基于RxJS createSelector的工具函数TypeScript类型定义问题
问题:为RxJS createSelector工具函数添加TypeScript类型时的类型不匹配错误
我基于RxJS的createSelector实现了一个工具函数,初始无类型代码如下:
getStoreView(operators: any): Observable<any> { const selector = createSelector(operators); const storeView = this.store.select(selector); return storeView; }
为了在编译期捕获调用错误,我添加了TypeScript类型定义,先定义辅助类型:
type Selectors = Record<string, Selector<State<AppState>, unknown>>; type Observables<Type extends Selectors> = Observable<{ [Property in keyof Type as Property]: ReturnType<Type[Property]> }>;
随后重构函数为:
getStoreViewNew<T extends Selectors>(operators: T): Observables<T> { const selector = createSelector(operators); const storeView = this.store.select(selector); return storeView; }
目前createSelector调用无类型问题,但this.store.select和返回值均报类型错误。我想确认:是否已正确传达函数的类型意图——接收由不同返回类型函数组成的Record参数,返回结构相同的Record的Observable,每个字段类型对应参数对应字段的返回类型?若意图正确,当前问题是什么,该如何解决?
错误信息
const selector: MemoizedSelector<T extends Record<string, Selector<infer S, unknown>> ? S : never, { [Key in keyof T]: T[Key] extends Selector<T extends Record<string, Selector<infer S, unknown>> ? S : never, infer R> ? R : never; }, never> No overload matches this call. Overload 1 of 9, '(mapFn: (state: AppState) => { [Key in keyof T]: T[Key] extends Selector<T extends Record<string, Selector<infer S, unknown>> ? S : never, infer R> ? R : never; }): Observable<...>', gave the following error. Argument of type 'MemoizedSelector<T extends Record<string, Selector<infer S, unknown>> ? S : never, { [Key in keyof T]: T[Key] extends Selector<T extends Record<string, Selector<infer S, unknown>> ? S : never, infer R> ? R : never; }, never>' is not assignable to parameter of type '(state: AppState) => { [Key in keyof T]: T[Key] extends Selector<T extends Record<string, Selector<infer S, unknown>> ? S : never, infer R> ? R : never; }'. Types of parameters 'state' and 'state' are incompatible. Type 'AppState' is not assignable to type 'T extends Record<string, Selector<infer S, unknown>> ? S : never'. Overload 2 of 9, '(key: keyof AppState): Observable<Heroes | States>', gave the following error. Argument of type 'MemoizedSelector<T extends Record<string, Selector<infer S, unknown>> ? S : never, { [Key in keyof T]: T[Key] extends Selector<T extends Record<string, Selector<infer S, unknown>> ? S : never, infer R> ? R : never; }, never>' is not assignable to parameter of type 'keyof AppState'.ts(2769)
解答
类型意图确认
你的类型意图是正确的,但辅助类型和泛型约束存在模糊性,导致TypeScript无法正确关联MemoizedSelector的输入状态类型与AppState。
问题根源
Selectors类型虽然指定了选择器输入为State<AppState>,但泛型推断时TypeScript没有将选择器的输入状态固定为AppState,而是尝试推断未知的泛型参数S,导致MemoizedSelector的输入类型与store.select期望的AppState不匹配。Observables类型使用ReturnType<Type[Property]>获取返回类型,但没有与createSelector生成的选择器输出类型做严格对齐。
解决方法
调整类型定义,明确绑定选择器的输入状态为AppState,同时利用createSelector的原生类型推断能力:
- 定义更精确的选择器映射类型:
type SelectorMap = Record<string, Selector<AppState, unknown>>;
- 简化函数泛型约束,让TypeScript自动推导返回类型:
getStoreViewNew<T extends SelectorMap>(operators: T) { const selector = createSelector(operators); return this.store.select(selector); }
此时TypeScript会自动推断返回类型为Observable<{ [K in keyof T]: ReturnType<T[K]> }>,完全符合你的需求——返回结构与输入Record一致的Observable,每个字段类型对应输入选择器的返回类型。
如果需要显式指定返回类型,可定义辅助类型:
type ObservableFromSelectors<T extends SelectorMap> = Observable<{ [K in keyof T]: T[K] extends Selector<infer _, infer R> ? R : never; }>; getStoreViewNew<T extends SelectorMap>(operators: T): ObservableFromSelectors<T> { const selector = createSelector(operators); return this.store.select(selector) as ObservableFromSelectors<T>; }
关键说明
- 明确选择器的输入状态必须是
AppState,消除类型推断的模糊性,让store.select识别到选择器的输入类型与自身状态匹配。 - 优先利用
createSelector的原生类型推断,无需手动构造返回类型,减少类型冗余和错误概率。
内容的提问来源于stack exchange,提问作者Árpád Magosányi
相关产品推荐
相关产品推荐

