Skip to content

fix(typings): fix typings for error prone operators like concatAll #2690

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

Merged
merged 1 commit into from
Jun 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 48 additions & 44 deletions spec/helpers/marble-testing.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,49 @@
import {Observable} from '../../dist/cjs/Observable';
import {SubscriptionLog} from '../../dist/cjs/testing/SubscriptionLog';
import {ColdObservable} from '../../dist/cjs/testing/ColdObservable';
import {HotObservable} from '../../dist/cjs/testing/HotObservable';
import {TestScheduler, observableToBeFn, subscriptionLogsToBeFn} from '../../dist/cjs/testing/TestScheduler';

declare const global: any;

export const rxTestScheduler: TestScheduler = global.rxTestScheduler;

export function hot(marbles: string, values?: any, error?: any): HotObservable<any> {
if (!global.rxTestScheduler) {
throw 'tried to use hot() in async test';
}
return global.rxTestScheduler.createHotObservable.apply(global.rxTestScheduler, arguments);
}

export function cold(marbles: string, values?: any, error?: any): ColdObservable<any> {
if (!global.rxTestScheduler) {
throw 'tried to use cold() in async test';
}
return global.rxTestScheduler.createColdObservable.apply(global.rxTestScheduler, arguments);
}

export function expectObservable(observable: Observable<any>,
unsubscriptionMarbles: string = null): ({ toBe: observableToBeFn }) {
if (!global.rxTestScheduler) {
throw 'tried to use expectObservable() in async test';
}
return global.rxTestScheduler.expectObservable.apply(global.rxTestScheduler, arguments);
}

export function expectSubscriptions(actualSubscriptionLogs: SubscriptionLog[]): ({ toBe: subscriptionLogsToBeFn }) {
if (!global.rxTestScheduler) {
throw 'tried to use expectSubscriptions() in async test';
}
return global.rxTestScheduler.expectSubscriptions.apply(global.rxTestScheduler, arguments);
}

export function time(marbles: string): number {
if (!global.rxTestScheduler) {
throw 'tried to use time() in async test';
}
return global.rxTestScheduler.createTime.apply(global.rxTestScheduler, arguments);
import {Observable} from '../../dist/cjs/Observable';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea why this file diffed so badly... but this adds two overloads for hot and cold to help with type inference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line endings?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just pushed a change to line endings and it didn't change anything :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... it's hard to review like this.

Is this maybe a thing from working on a windows machine?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had reset line endings on the file and it didn't change the diff. It diffs just fine for me locally too :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you comment on what's changed in this file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two new overloads for hot and cold.

export function hot(marbles: string, values?: void, error?: any): HotObservable<string>;
export function hot<V>(marbles: string, values?: { [index: string]: V; }, error?: any): HotObservable<V>;


export function cold(marbles: string, values?: void, error?: any): ColdObservable<string>;
export function cold<V>(marbles: string, values?: { [index: string]: V; }, error?: any): ColdObservable<V>;

The first overload, defaults the returned HotObservable to be a type of string (ie ColdObservable<string>)
The second overload, allows for type inference of the output observable, so that it is typed as a union of the input values.

Given a value let values = { a: 1, b: 2, c: 3 } then the type of hot('a---b---c---|', values) will then HotObservable<number>;

Given a more complex case (stolen from mergeAll-spec.ts)

it('should merge two cold Observables at a time with parameter concurrency=2', () => {
    const x = cold(     'a---b---c---|        ');
    const xsubs =     '  ^           !        ';
    const y = cold(        'd---e---f---|     ');
    const ysubs =     '     ^           !     ';
    const z = cold(                 '--g---h-|');
    const zsubs =     '              ^       !';
    const e1 =    hot('--x--y--z--|           ', { x, y, z });
    const e1subs =    '^                     !';
    const expected =  '--a--db--ec--f--g---h-|';

    expectObservable(e1.mergeAll(2)).toBe(expected);
    expectSubscriptions(x.subscriptions).toBe(xsubs);
    expectSubscriptions(y.subscriptions).toBe(ysubs);
    expectSubscriptions(z.subscriptions).toBe(zsubs);
    expectSubscriptions(e1.subscriptions).toBe(e1subs);
  });

Given that x, y, and z are all ColdObservable<string> the type of e1 then becomes HotObservable<ColdObservable<string>>. This then gives us proper type information in unit tests and allows removing of several corner cases where we explicitly cast to any to work around the problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see ... looks good.

import {SubscriptionLog} from '../../dist/cjs/testing/SubscriptionLog';
import {ColdObservable} from '../../dist/cjs/testing/ColdObservable';
import {HotObservable} from '../../dist/cjs/testing/HotObservable';
import {TestScheduler, observableToBeFn, subscriptionLogsToBeFn} from '../../dist/cjs/testing/TestScheduler';

declare const global: any;

export const rxTestScheduler: TestScheduler = global.rxTestScheduler;

export function hot(marbles: string, values?: void, error?: any): HotObservable<string>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uh, values is void type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we've used it before for things like groupBy and bindNodeCallback

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather values it void by default (if we don't give it a value), if we do give it a value we can then infer it's type.

export function hot<V>(marbles: string, values?: { [index: string]: V; }, error?: any): HotObservable<V>;
export function hot<V>(marbles: string, values?: { [index: string]: V; } | void, error?: any): HotObservable<any> {
if (!global.rxTestScheduler) {
throw 'tried to use hot() in async test';
}
return global.rxTestScheduler.createHotObservable.apply(global.rxTestScheduler, arguments);
}

export function cold(marbles: string, values?: void, error?: any): ColdObservable<string>;
export function cold<V>(marbles: string, values?: { [index: string]: V; }, error?: any): ColdObservable<V>;
export function cold<V>(marbles: string, values?: { [index: string]: V; } | void, error?: any): ColdObservable<V> {
if (!global.rxTestScheduler) {
throw 'tried to use cold() in async test';
}
return global.rxTestScheduler.createColdObservable.apply(global.rxTestScheduler, arguments);
}

export function expectObservable(observable: Observable<any>,
unsubscriptionMarbles: string = null): ({ toBe: observableToBeFn }) {
if (!global.rxTestScheduler) {
throw 'tried to use expectObservable() in async test';
}
return global.rxTestScheduler.expectObservable.apply(global.rxTestScheduler, arguments);
}

export function expectSubscriptions(actualSubscriptionLogs: SubscriptionLog[]): ({ toBe: subscriptionLogsToBeFn }) {
if (!global.rxTestScheduler) {
throw 'tried to use expectSubscriptions() in async test';
}
return global.rxTestScheduler.expectSubscriptions.apply(global.rxTestScheduler, arguments);
}

export function time(marbles: string): number {
if (!global.rxTestScheduler) {
throw 'tried to use time() in async test';
}
return global.rxTestScheduler.createTime.apply(global.rxTestScheduler, arguments);
}
Loading