Picci
Picci

Reputation: 17762

`race` function raising Typescript exception even if it works perfectly

I have 2 Observables. The first one emits numbers the second emits strings.

These 2 Observables start emitting when some triggers emit. The triggers are Subjects and each of the 2 Observables has its own trigger.

I want to start a race between the 2 Observables so that the Observable whose trigger emits first wins and start emitting, while the other is ignored even if its trigger subsequently emits.

This is the code

const observableOfNumber = interval(100).pipe(take(10));
const observableOfNumberFunction = () => observableOfNumber;

const observableOfString = interval(100).pipe(map(number => String.fromCharCode(65 + number)), take(10));
const observableOfStringFunction = ()  => observableOfString;

const startEmittingNumbers = new Subject<any>()
const emittingNumbers = startEmittingNumbers
                        .pipe(
                            map(() => observableOfNumberFunction),
                        );

const startEmittingChars = new Subject<any>()
const emittingChars = startEmittingChars
                        .pipe(
                            map(() => observableOfStringFunction),
                        );


race(emittingNumbers, emittingChars)
.pipe(
    mergeMap(handler => handler())
)
.subscribe(console.log);

This code make Typescript complain with the following error

enter image description here

The code though seems to work fine in its essence.

If I just explicitly declare the return type of observableOfStringFunctionas Observable<any>, i.e.

const observableOfStringFunction = (): Observable<any>  => observableOfString;

Typescript stops complaining and everything works perfectly.

Similarly, if rather than using race I use merge, Typescript does not complain even if, from a type-checking point of view, merge and race should behave the same.

Can anybody point me to the reason of this different behavior?

Upvotes: 0

Views: 254

Answers (1)

Michael Doye
Michael Doye

Reputation: 8171

This happens because the type that race returns is the same as the type which emits first from its parameters (ie, the race winner) - Observable<T>.

In your example the race winner is of type number because the Type Checker is probably (possibly) evaluating the first arguments' type as the return type as well, and then expects each subsequent emission to be of the same type (which it isn't). If you were to switch your arguments around, you would notice that IntelliSense will give you the same error for your emittingNumbers param. That is why the IDE complains but the code still runs.

Here is a more explicit example:

const obs1 = interval(1000).pipe(mapTo('fast one')); // Emits first, type string
const obs2 = interval(3000).pipe(mapTo(1)); // Type number, TypeScript will complain
const obs3 = interval(5000).pipe(mapTo('slow one'));

race(obs3, obs1, obs2)
.subscribe(
  winner => console.log(winner)
);

const {
  Observable, 
  interval,
  Subject,
  race
} = rxjs;
const { 
  map, 
  take, 
  mergeMap,
  mapTo 
} = rxjs.operators;

const obs1 = interval(1000).pipe(mapTo('fast one'));
const obs2 = interval(3000).pipe(mapTo(1)); // TypeScript will complain about this
const obs3 = interval(5000).pipe(mapTo('slow one'));

race(obs3, obs1, obs2)
.subscribe(
  winner => console.log(winner)
);
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>

Upvotes: 2

Related Questions