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.
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.
Most helpful comment
OK, now the example will work if you replace
item => isFirst(item)withisFirst.We don't infer something as a type predicate unless it's explicitly declared as one (#5101). So
item => isFirst(item)just returns abooleanbutisFIrstis a type predicate because it was declared as one.