Typescript: Type guards are not working properly in Observable

Created on 19 Oct 2017  路  9Comments  路  Source: microsoft/TypeScript




TypeScript Version: 2.5.3

Code

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/filter';

type AType = 'first' | 'second';

function isFirst(value: AType): value is 'first' {
  return value === 'first';
}

const inputs: AType[] = ['first', 'second'];
const obx = Observable.from(inputs);

obx
  .filter(item => isFirst(item)) // <- checking types with guard
  .subscribe(item => console.log(item)); // <- here item is still `'first' | 'second'`!

A simple example to reproduce.

Expected behavior:
Type guards must make sure the flow continues according to the current status of types.

Actual behavior:
Apparently because the type guard is not within a branch it is not working as intended, or it is working as intended by design that I personally think is not a good idea.

Question

Most helpful comment

OK, now the example will work if you replace item => isFirst(item) with isFirst.
We don't infer something as a type predicate unless it's explicitly declared as one (#5101). So item => isFirst(item) just returns a boolean but isFIrst is a type predicate because it was declared as one.

All 9 comments

I think this could be done by updating the rxjs types. If filter is just declared to take (item: T) => boolean then TypeScript won't know that it's a filter and can't be smart, but it could be declared to take (item: T) => T is S just like [].filter is:

// from lib.es5.d.ts
filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];

What version of rxjs are you using? Observable.from and .filter don't seem to be in 5.5.0 or 6.6.0-alpha.0.

@andy-ms I'm using [email protected], and tried with 5.5.0 but no difference!
from rxjs:

import { Observable } from '../Observable';
export declare function filter<T, S extends T>(this: Observable<T>, predicate: (value: T, index: number) => value is S, thisArg?: any): Observable<S>;
export declare function filter<T>(this: Observable<T>, predicate: (value: T, index: number) => boolean, thisArg?: any): Observable<T>;

I found this issue. Is this a duplication?

Even with 5.4.3 I still can't get your example working:

src/a.ts(11,24): error TS2339: Property 'from' does not exist on type 'typeof Observable'.

And even if I change that line to declare const obx: Observable<any>;, I still can't access a filter method.
Is it possible you have some extension installed?

@andy-ms you're missing the from -> import 'rxjs/add/observable/from'

I updated my question :)

OK, now the example will work if you replace item => isFirst(item) with isFirst.
We don't infer something as a type predicate unless it's explicitly declared as one (#5101). So item => isFirst(item) just returns a boolean but isFIrst is a type predicate because it was declared as one.

@andy-ms that's interesting! Thanks a lot.

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CyrusNajmabadi picture CyrusNajmabadi  路  3Comments

wmaurer picture wmaurer  路  3Comments

Roam-Cooper picture Roam-Cooper  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

weswigham picture weswigham  路  3Comments