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

基于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。

问题根源

  1. Selectors类型虽然指定了选择器输入为State<AppState>,但泛型推断时TypeScript没有将选择器的输入状态固定为AppState,而是尝试推断未知的泛型参数S,导致MemoizedSelector的输入类型与store.select期望的AppState不匹配。
  2. Observables类型使用ReturnType<Type[Property]>获取返回类型,但没有与createSelector生成的选择器输出类型做严格对齐。

解决方法

调整类型定义,明确绑定选择器的输入状态为AppState,同时利用createSelector的原生类型推断能力:

  1. 定义更精确的选择器映射类型:
type SelectorMap = Record<string, Selector<AppState, unknown>>;
  1. 简化函数泛型约束,让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:21:24