admin管理员组

文章数量:1430053

Typescript supports discriminated unions. How to extend the same concept with Rxjs to the filter operator in below example?

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')
);

I above code snippet, I would want to see newObs$ to have its type inferred as: Observable<Square>. But apparently, TypeScript doesn't do that.

How to achieve this? Am I reaching the limits of TypeScript type inference?

I look for this as it seems to be very useful in a Redux + Redux-Observable codebase.

Typescript supports discriminated unions. How to extend the same concept with Rxjs to the filter operator in below example?

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')
);

I above code snippet, I would want to see newObs$ to have its type inferred as: Observable<Square>. But apparently, TypeScript doesn't do that.

How to achieve this? Am I reaching the limits of TypeScript type inference?

I look for this as it seems to be very useful in a Redux + Redux-Observable codebase.

Share Improve this question edited May 25, 2018 at 9:55 Harshal Patil asked May 25, 2018 at 8:44 Harshal PatilHarshal Patil 21.2k16 gold badges73 silver badges150 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 8

Actually you can do this with TypeScript type guards. See section "Type Guards and Differentiating Types" at http://www.typescriptlang/docs/handbook/advanced-types.html

The key here is the function isWhatever(x: any): x is Whatever => ... syntax.

This basically says that if the isWhatever function returns true then it guarantees that x is of Whatever type.

In your example TypeScript considers all three classes:

So you can define the predicate function for filter() as this:

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

Now it will properly consider only the Square class:

See live demo: https://stackblitz./edit/rxjs6-demo-z9lwxe?file=index.ts

Very similar question: https://github./ReactiveX/rxjs/issues/2340

This is not necessarily a limitation with the TypeScript type system but rather with the implementation of filter. You can easily achieve the desired behaviour using flatMap:

// Inferred type: Observable<Square>
const newObs$ = obs$.pipe(
  flatMap((x) => x.kind === "square" ? of(x) : empty())
);

本文标签: javascriptTypescript discriminated union types with Rxjs filter operatorStack Overflow