使用Rx.js过滤运算符的Typescript区分联合类型?

时间:2018-05-25 08:44:30

标签: javascript typescript redux rxjs redux-observable

Typescript支持discriminated unions如何在下面的示例中将Rxjs的相同概念扩展到filter运算符

interface Square {
    kind: 'square';
    width: number;
}

interface Circle {
    kind: 'circle';
    radius: number;
}

interface Center {
    kind: 'center';
}

type Shape = Square | Circle | Center;

const obs$: Observable<Shape> = of<Shape>({ kind: 'square', width: 10 });

// Expected type: Observable<Square>
// Actual type: Observable<Shape>
const newObs$ = obs$.pipe(
    filter((x) => x.kind === 'square')
);

我在上面的代码片段中,我希望看到newObs $将其类型推断为:Observable<Square>。但显然,TypeScript并没有这样做。

如何实现这一目标?我是否达到了TypeScript类型推断的极限?

我在寻找这个,因为它似乎在Redux + Redux-Observable代码库中非常有用。

2 个答案:

答案 0 :(得分:4)

实际上,您可以使用TypeScript类型防护来执行此操作。请参阅http://www.typescriptlang.org/docs/handbook/advanced-types.html

中的“类型保护和区分类型”部分

这里的关键是function isWhatever(x: any): x is Whatever => ...语法。

这基本上表明,如果isWhatever函数返回true,则可以保证x属于Whatever类型。

在您的示例中,TypeScript会考虑所有三个类:

without-typeguard

因此,您可以为filter()定义谓词函数:

filter((x: Shape): x is Square => x.kind === 'square')

现在它将只考虑Square类:

with-typeguard

查看现场演示:https://stackblitz.com/edit/rxjs6-demo-z9lwxe?file=index.ts

非常相似的问题:https://github.com/ReactiveX/rxjs/issues/2340

答案 1 :(得分:0)

这不一定是TypeScript类型系统的限制,而是filter的实现。您可以使用flatMap

轻松实现所需的行为
// Inferred type: Observable<Square>
const newObs$ = obs$.pipe(
  flatMap((x) => x.kind === "square" ? of(x) : empty())
);