ReactiveX/rxjs

Inconsistent iif type checking behavior in typescript 5.0.2

moniuch opened this issue · 3 comments

Describe the bug

Given the below snippet

const source$ = from([{ firstName: 'Brian', lastName: 'Smith' }, null]);
const err = new Error('my error');

const final$ = source$.pipe(
  switchMap((o) =>
    iif(
      () => !o,
      throwError(() => err),
      of(`${o.firstName} ${o.lastName}`)
    )
  )
);

I'm getting different results depending on IDE, and I am not sure I'm using the operators correctly
(rxjs 7.8.0 and - typescript 5.0.2 in both cases)

VSCode (complains about o being possibly null)
image

Stackblitz
image

In both cases final$ is reported as Observable<string>

Expected behavior

Given that we catch the null condition in iif the typescript should not complain in the truthy execution branch

Reproduction code

import { of, switchMap, from, throwError, iif } from 'rxjs';


const source$ = from([{ firstName: 'Brian', lastName: 'Smith' }, null]);
const err = new Error('my error');

const final$ = source$.pipe(
  switchMap((o) =>
    iif(
      () => !o,
      throwError(() => err),
      of(`${o.firstName} ${o.lastName}`)
    )
  )
);


### Reproduction URL

_No response_

### Version

7.8.0

### Environment

_No response_

### Additional context

_No response_

There are no errors on stackblitz because TypeScript does not run in strict mode on it by default:

I recommend simplifying to this:

import { of, switchMap, from } from 'rxjs';

const source$ = from([{ firstName: 'Brian', lastName: 'Smith' }, null]);

const final$ = source$.pipe(
  switchMap((o) => {
    if (!o) {
      throw new Error('my error');
    }

    return of(`${o.firstName} ${o.lastName}`);
  })
);

Or even to this:

import { from, map } from 'rxjs';

const source$ = from([{ firstName: 'Brian', lastName: 'Smith' }, null]);

const final$ = source$.pipe(
  map((o) => {
    if (!o) {
      throw new Error('my error');
    }

    return `${o.firstName} ${o.lastName}`;
  })
);

Ah ok, thanks for this strict: true lesson, I wasn't aware of it. Closing then. Sorry for the noise.

The most appropriate code for your version:

const source$ = from([{firstName: 'Brian', lastName: 'Smith'}, null]);
const err = new Error('my error');

const final$ = source$.pipe(
  switchMap((o) =>
    !o
      ? throwError(() => err)
      : of(`${o.firstName} ${o.lastName}`),
  )
);