首页 > 解决方案 > Typescript 函数重载不兼容

问题描述

我一直在实现 NgRx 商店,发现自己使用了该模式

 concatMap(action => of(action).pipe(
          withLatestFrom(this.store.pipe(select(fromBooks.getCollectionBookIds)))
        )),

(来自https://ngrx.io/guide/effects的底部)

考虑到它会占用多少空间,我想编写一个方便的实用程序运算符来处理它。

所以我想出了一个应该可行的实现:

export function concatMapLatestFrom<A extends Action>(
    ...xs: Array<ObservableInput<never>>
): OperatorFunction<A, unknown> {
    return function (source: Observable<A>): Observable<unknown> {
        return source.pipe(
            concatMap((action) => of(action).pipe(withLatestFrom(...xs))),
        );
    };
}

我写了一些正确类型的重载:

export function concatMapLatestFrom<X1, A extends Action>(
    source1: ObservableInput<X1>,
): { (source1: X1): OperatorFunction<A, [A, X1]> };

export function concatMapLatestFrom<X1, X2, A extends Action>(
    source1: ObservableInput<X1>,
    source2: ObservableInput<X2>,
): { (source1: X1, source2: X2): OperatorFunction<A, [A, X1, X2]> };

export function concatMapLatestFrom<X1, X2, X3, A extends Action>(
    source1: ObservableInput<X1>,
    source2: ObservableInput<X2>,
    source3: ObservableInput<X3>,
): {
    (source1: X1, source2: X2, source3: X3): OperatorFunction<
        A,
        [A, X1, X2, X3]
    >;
};

但由于某种原因,它认为重载签名与实现不兼容。如果我注释掉其中一个,它会重复下一个。

它使我无法理解这实际上有什么问题,编译器最好告诉我为什么它不兼容,但不幸的是它没有提供任何细节。

标签: typescriptrxjsngrx

解决方案


我认为罪魁祸首是你在那里使用的never和类型。unknown

所以,如果你有

of(1)
  .pipe(
    concatMapLatestFrom(of('john'), of(true))
  ).subscribe(observer)

那么,如果我理解正确,observer应该收到一个[number, string, boolean]元组。

据我所知,concatMapLatestFrom并没有说得很清楚:

function concatMapLatestFrom (): OperatorFunction<A, unknown> {}

因此,我们只得到了该元组的第一个元素,即源的类型。剩下要做的是根据ObservableInput提供给 s的类型来推断其他类型的属性concatMapLatestFrom

考虑到这一点,这将是我的方法:

export function concatMapLatestFrom<
  A extends Action,
  U extends Array<ObservableInput<any>>,
  R = Unshift<ObservedValueTupleFromArray<U>, A>
> (
  ...xs: U
): OperatorFunction<A, R> {
  return function (source: Observable<A>): Observable<R> {
    return source.pipe(
      concatMap((action) => of(action).pipe(withLatestFrom(...xs))),
    );
  };
}

使用的类型可以从以下位置导出'rxjs'


// Mapped tuples: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html#mapped-types-on-tuples-and-arrays
// E.g [Observable<number>, Observable<string>] --> [number, string]
export type ObservedValueTupleFromArray<X> =
  X extends Array<ObservableInput<any>>
  ? { [K in keyof X]: ObservedValueOf<X[K]> }
  : never;

// Simply extract the value
export type ObservedValueOf<O> = O extends ObservableInput<infer T> ? T : never;

// Unshift<[B, C], A> --> [A, B, C]
export type Unshift<X extends any[], Y> =
  ((arg: Y, ...rest: X) => any) extends ((...args: infer U) => any)
  ? U
  : never;

资源


推荐阅读