Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: filter and partition as typescript typeguard #2340

Closed
Bnaya opened this issue Feb 7, 2017 · 4 comments
Closed

Proposal: filter and partition as typescript typeguard #2340

Bnaya opened this issue Feb 7, 2017 · 4 comments

Comments

@Bnaya
Copy link

Bnaya commented Feb 7, 2017

filter and partition as typescript typeguard

When filtering or partitioning stream or events,
And the filter criteria will narrow down possible types,
the partition / filter returned Observable type should be narrowed down as-well

Given the following code:

export function typeGuard(root: HTMLElement) {
    return Observable.fromEvent<Event>(root, 'click', {capture: true})
    .filter(function (e) {
        return e instanceof MouseEvent;
    })
    .do(function (e) {
        // e should be of type MouseEvent
        // [ts] Property 'clientX' does not exist on type 'Event'
        e.clientX;
    });
}

The e parameter of do should be of type MouseEvent

How to implement:
Generics!
And maybe is possible with some inference magic

  • I think filter already has somekind of this functionality but the declaration it broken.
export declare function filter<T, S extends T>(this: Observable<T>, predicate: (value: T, index: number) => value is S, thisArg?: any): Observable<S>;
@kwonoj
Copy link
Member

kwonoj commented Feb 7, 2017

Unfortunately, as far as I know typescript's inference doesn't work automatically in this case and you may need supply generic types explicitly to narrow down types as you'd like to have.

Current type definition for filter supports does case and it is not broken, for example above snippet can supply generic types like

export function typeGuard(root: HTMLElement) {
    return Observable.fromEvent<Event>(root, 'click', {capture: true})
    .filter<Event, MouseEvent>((value: Event): value is MouseEvent => value instanceof MouseEvent)
    .do(function (e) {
        e.clientX; //now chained operator got narrowed down type <S> instead of original type <T>
    });
}

.

I'll leave this opened bit more and /cc @david-driscoll just in case if there's any possible improvement.

@david-driscoll
Copy link
Member

@Bnaya I think @kwonoj is correct here. While value instanceof X is a valid typeguard, the actual method itself is not a type guard, so it behaves exactly as it should, just a normal filter function.

If you make the method explicitly a typeguard it should work. I don't think the concept of implicit typeguard exists in TypeScript (I could be wrong). In order for this to work any other way would most likely require a compiler change which would "promote" the inner typeguard (collection of type guards creating a union type??) to the parent method.

@Bnaya
Copy link
Author

Bnaya commented Feb 8, 2017

Thanks!

@arielshaqed
Copy link

@kwonoj do you have a suggestion how to handle partition? AFAIK TypeScript has no way of specifying "if function returns true, type is A, otherwise type is B.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 12, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants