具有打字稿联合类型的 RxJS 自定义运算符

RxJS Custom operator with typescript Union type

我有以下型号:

export interface IModel<T> {
 exp: number,
 value: T
}

我想创建一个自定义 RxJS 运算符,例如:

private customOperator<U extends A | B, T extends IModel<U>>(custom: Observable<T>): Observable<U> {
    return custom.pipe(map((x: T) => x.value ));
  }

但是我在使用的时候遇到了类型错误:

  mySub:  = new Subject<IModel<A>>;
  myObs$: Observable<A> = this.mySub.asObservable().pipe(this.customOperator); // <== ERROR

错误:无法将类型 'Observable<A | B>' 分配给类型 'Observable< A>'。

知道如何更改我的自定义运算符来避免该问题吗?

将运算符包装在工厂函数中。

private customOperator<T extends IModel<U>, U extends A | B> () {
  return (custom: Observable<T>): Observable<U> =>
   custom.pipe(map((x: T) => x.value ));
}

然后在管道中使用运算符作为函数调用而不是函数引用。

myObs$: Observable<A> = this.mySub.asObservable().pipe(this.customOperator());

干杯

您需要使用 Typescript type assertion 功能。

在你的情况下,编译器会抱怨,因为 myObs$ 必须 Observable<A> 类型,而你的自定义运算符可以 return Observable<A> Observable<B>.

因此,如果您想避免错误,您需要向 Typescript 保证您的自定义运算符肯定会 return 和 Observable<A>。这是避免对 Typescript 编译器进行合法检查的技巧,因此您最好确保自己做的事情是正确的。

所以代码应该是这样的

myObs$: Observable<A> = this.mySub.asObservable().pipe(this.customOperator) as Observable<A>;

或者,更简单地说

myObs$ = this.mySub.asObservable().pipe(this.customOperator) as Observable<A>;

如果您将鼠标悬停在 myObs$ 上,例如在 VSCode 内,您将看到为 myObs$ 推断的类型实际上是 Observable<A>,即使您没有指定类型(至少在代码的最后一个版本中)

更新基于@akotech 回复

处理这种情况的更好方法是@akotech 提出的方法。

customOperator 编码如下 returns 和 Observable<U>

private customOperator<T extends IModel<U>, U extends A | B> () {
  return (custom: Observable<T>): Observable<U> =>
   custom.pipe(map((x: T) => x.value ));
}

有趣的是,至少在我的情况下,如果我没有像这一行那样声明 myObs$ 的类型

myObs$ = mySub.pipe(customOperator_())

myObs$ 的推断类型是 Observable<A | B>,但与此同时,如果我尝试这样的事情

myObs$: : Observable<B> = mySub.pipe(customOperator_())

我从编译器中得到一个错误 Type 'Observable<IModel>' is not assignable to type 'Observable<IModel>',这是正确的,因为 mySub 通知对象类型 IModel<A>.

同时,如果我尝试这样的事情

myObs$: : Observable<A> = mySub_A.pipe(customOperator_())

编译器没有引发错误,这也是正确的。

所以@akotech提出的方案比我提出的方案更安全