Skip to content

Commit 3240419

Browse files
committed
feat(last): simplify interface
- Removes resultSelector argument - Updates tests BREAKING CHANGE: no longer accepts `resultSelector` argument. To get this same functionality, use `map`.
1 parent a011338 commit 3240419

File tree

3 files changed

+31
-131
lines changed

3 files changed

+31
-131
lines changed

spec/operators/last-spec.ts

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect } from 'chai';
1+
22
import * as Rx from '../../src/Rx';
33
import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing';
44

@@ -87,7 +87,7 @@ describe('Observable.prototype.last', () => {
8787
const e1subs = '(^!)';
8888
const expected = '(a|)';
8989

90-
expectObservable(e1.last(null, null, 'a')).toBe(expected);
90+
expectObservable(e1.last(null, 'a')).toBe(expected);
9191
expectSubscriptions(e1.subscriptions).toBe(e1subs);
9292
});
9393

@@ -96,23 +96,7 @@ describe('Observable.prototype.last', () => {
9696
const e1subs = '^ !';
9797
const expected = '----------------(d|)';
9898

99-
expectObservable(e1.last(null, null, 'x')).toBe(expected);
100-
expectSubscriptions(e1.subscriptions).toBe(e1subs);
101-
});
102-
103-
it('should support a result selector argument', () => {
104-
const e1 = hot('--a--^---b---c---d---e--|');
105-
const e1subs = '^ !';
106-
const expected = '-------------------(x|)';
107-
108-
const predicate = function (x) { return x === 'c'; };
109-
const resultSelector = function (x, i) {
110-
expect(i).to.equal(1);
111-
expect(x).to.equal('c');
112-
return 'x';
113-
};
114-
115-
expectObservable(e1.last(predicate, resultSelector)).toBe(expected);
99+
expectObservable(e1.last(null, 'x')).toBe(expected);
116100
expectSubscriptions(e1.subscriptions).toBe(e1subs);
117101
});
118102

@@ -133,20 +117,6 @@ describe('Observable.prototype.last', () => {
133117
expectSubscriptions(e1.subscriptions).toBe(e1subs);
134118
});
135119

136-
it('should raise error when result selector throws', () => {
137-
const e1 = hot('--a--^---b---c---d---e--|');
138-
const e1subs = '^ ! ';
139-
const expected = '--------# ';
140-
141-
const predicate = function (x) { return x === 'c'; };
142-
const resultSelector = function (x, i) {
143-
throw 'error';
144-
};
145-
146-
expectObservable(e1.last(predicate, resultSelector)).toBe(expected);
147-
expectSubscriptions(e1.subscriptions).toBe(e1subs);
148-
});
149-
150120
it('should support type guards without breaking previous behavior', () => {
151121
// tslint:disable no-unused-variable
152122

@@ -203,16 +173,6 @@ describe('Observable.prototype.last', () => {
203173
// boolean predicates preserve the type
204174
xs.last(x => typeof x === 'string')
205175
.subscribe(x => x); // x is still string | number
206-
xs.last(x => !!x, x => x)
207-
.subscribe(x => x); // x is still string | number
208-
xs.last(x => typeof x === 'string', x => x, '') // default is string; x remains string | number
209-
.subscribe(x => x); // x is still string | number
210-
211-
// `last` still uses the `resultSelector` return type, if it exists.
212-
xs.last(x => typeof x === 'string', x => ({ str: `${x}` })) // x remains string | number
213-
.subscribe(o => o.str); // o is { str: string }
214-
xs.last(x => typeof x === 'string', x => ({ str: `${x}` }), { str: '' })
215-
.subscribe(o => o.str); // o is { str: string }
216176
}
217177

218178
// tslint:disable enable

src/internal/operators/last.ts

Lines changed: 22 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,8 @@
11
import { Observable } from '../Observable';
22
import { Operator } from '../Operator';
33
import { Subscriber } from '../Subscriber';
4-
import { EmptyError } from '../util/EmptyError';
5-
import { OperatorFunction, MonoTypeOperatorFunction } from '../types';
6-
7-
/* tslint:disable:max-line-length */
8-
export function last<T, S extends T>(predicate: (value: T, index: number, source: Observable<T>) => value is S): OperatorFunction<T, S>;
9-
export function last<T, S extends T, R>(predicate: (value: T | S, index: number, source: Observable<T>) => value is S,
10-
resultSelector: (value: S, index: number) => R, defaultValue?: R): OperatorFunction<T, R>;
11-
export function last<T, S extends T>(predicate: (value: T, index: number, source: Observable<T>) => value is S,
12-
resultSelector: void,
13-
defaultValue?: S): OperatorFunction<T, S>;
14-
export function last<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean): MonoTypeOperatorFunction<T>;
15-
export function last<T, R>(predicate: (value: T, index: number, source: Observable<T>) => boolean,
16-
resultSelector?: (value: T, index: number) => R,
17-
defaultValue?: R): OperatorFunction<T, R>;
18-
export function last<T>(predicate: (value: T, index: number, source: Observable<T>) => boolean,
19-
resultSelector: void,
20-
defaultValue?: T): MonoTypeOperatorFunction<T>;
21-
/* tslint:enable:max-line-length */
4+
import { EmptyError } from '..//util/EmptyError';
5+
import { MonoTypeOperatorFunction } from '../../internal/types';
226

237
/**
248
* Returns an Observable that emits only the last item emitted by the source Observable.
@@ -30,28 +14,26 @@ export function last<T>(predicate: (value: T, index: number, source: Observable<
3014
*
3115
* @throws {EmptyError} Delivers an EmptyError to the Observer's `error`
3216
* callback if the Observable completes before any `next` notification was sent.
33-
* @param {function} predicate - The condition any source emitted item has to satisfy.
17+
* @param {function} [predicate] - The condition any source emitted item has to satisfy.
18+
* @param {any} [defaultValue] - An optional default value to provide if last
19+
* predicate isn't met or no values were emitted.
3420
* @return {Observable} An Observable that emits only the last item satisfying the given condition
3521
* from the source, or an NoSuchElementException if no such items are emitted.
3622
* @throws - Throws if no items that match the predicate are emitted by the source Observable.
37-
* @method last
38-
* @owner Observable
3923
*/
40-
export function last<T, R>(predicate?: (value: T, index: number, source: Observable<T>) => boolean,
41-
resultSelector?: ((value: T, index: number) => R) | void,
42-
defaultValue?: R): OperatorFunction<T, T | R> {
43-
return (source: Observable<T>) => source.lift(new LastOperator(predicate, resultSelector, defaultValue, source));
24+
export function last<T>(predicate?: (value: T, index: number, source: Observable<T>) => boolean,
25+
defaultValue?: T): MonoTypeOperatorFunction<T> {
26+
return (source: Observable<T>) => source.lift(new LastOperator(predicate, defaultValue, source));
4427
}
4528

46-
class LastOperator<T, R> implements Operator<T, R> {
47-
constructor(private predicate?: (value: T, index: number, source: Observable<T>) => boolean,
48-
private resultSelector?: ((value: T, index: number) => R) | void,
49-
private defaultValue?: any,
50-
private source?: Observable<T>) {
29+
class LastOperator<T> implements Operator<T, T> {
30+
constructor(private predicate: (value: T, index: number, source: Observable<T>) => boolean,
31+
private defaultValue: any,
32+
private source: Observable<T>) {
5133
}
5234

53-
call(observer: Subscriber<R>, source: any): any {
54-
return source.subscribe(new LastSubscriber(observer, this.predicate, this.resultSelector, this.defaultValue, this.source));
35+
call(observer: Subscriber<T>, source: any): any {
36+
return source.subscribe(new LastSubscriber(observer, this.predicate, this.defaultValue, this.source));
5537
}
5638
}
5739

@@ -60,16 +42,15 @@ class LastOperator<T, R> implements Operator<T, R> {
6042
* @ignore
6143
* @extends {Ignored}
6244
*/
63-
class LastSubscriber<T, R> extends Subscriber<T> {
64-
private lastValue: T | R;
65-
private hasValue: boolean = false;
66-
private index: number = 0;
45+
class LastSubscriber<T> extends Subscriber<T> {
46+
private lastValue: T;
47+
private hasValue = false;
48+
private index = 0;
6749

68-
constructor(destination: Subscriber<R>,
69-
private predicate?: (value: T, index: number, source: Observable<T>) => boolean,
70-
private resultSelector?: ((value: T, index: number) => R) | void,
71-
private defaultValue?: any,
72-
private source?: Observable<T>) {
50+
constructor(destination: Subscriber<T>,
51+
private predicate: (value: T, index: number, source: Observable<T>) => boolean,
52+
private defaultValue: T,
53+
private source: Observable<T>) {
7354
super(destination);
7455
if (typeof defaultValue !== 'undefined') {
7556
this.lastValue = defaultValue;
@@ -82,10 +63,6 @@ class LastSubscriber<T, R> extends Subscriber<T> {
8263
if (this.predicate) {
8364
this._tryPredicate(value, index);
8465
} else {
85-
if (this.resultSelector) {
86-
this._tryResultSelector(value, index);
87-
return;
88-
}
8966
this.lastValue = value;
9067
this.hasValue = true;
9168
}
@@ -100,27 +77,11 @@ class LastSubscriber<T, R> extends Subscriber<T> {
10077
return;
10178
}
10279
if (result) {
103-
if (this.resultSelector) {
104-
this._tryResultSelector(value, index);
105-
return;
106-
}
10780
this.lastValue = value;
10881
this.hasValue = true;
10982
}
11083
}
11184

112-
private _tryResultSelector(value: T, index: number) {
113-
let result: any;
114-
try {
115-
result = (<any>this).resultSelector(value, index);
116-
} catch (err) {
117-
this.destination.error(err);
118-
return;
119-
}
120-
this.lastValue = result;
121-
this.hasValue = true;
122-
}
123-
12485
protected _complete(): void {
12586
const destination = this.destination;
12687
if (this.hasValue) {
Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,6 @@
11
import { Observable } from '../../Observable';
22
import { last as higherOrder } from '../../operators/last';
33

4-
/* tslint:disable:max-line-length */
5-
export function last<T, S extends T>(this: Observable<T>,
6-
predicate: (value: T, index: number, source: Observable<T>) => value is S): Observable<S>;
7-
export function last<T, S extends T, R>(this: Observable<T>,
8-
predicate: (value: T | S, index: number, source: Observable<T>) => value is S,
9-
resultSelector: (value: S, index: number) => R, defaultValue?: R): Observable<R>;
10-
export function last<T, S extends T>(this: Observable<T>,
11-
predicate: (value: T, index: number, source: Observable<T>) => value is S,
12-
resultSelector: void,
13-
defaultValue?: S): Observable<S>;
14-
export function last<T>(this: Observable<T>,
15-
predicate?: (value: T, index: number, source: Observable<T>) => boolean): Observable<T>;
16-
export function last<T, R>(this: Observable<T>,
17-
predicate: (value: T, index: number, source: Observable<T>) => boolean,
18-
resultSelector?: (value: T, index: number) => R,
19-
defaultValue?: R): Observable<R>;
20-
export function last<T>(this: Observable<T>,
21-
predicate: (value: T, index: number, source: Observable<T>) => boolean,
22-
resultSelector: void,
23-
defaultValue?: T): Observable<T>;
24-
/* tslint:enable:max-line-length */
25-
264
/**
275
* Returns an Observable that emits only the last item emitted by the source Observable.
286
* It optionally takes a predicate function as a parameter, in which case, rather than emitting
@@ -33,15 +11,16 @@ export function last<T>(this: Observable<T>,
3311
*
3412
* @throws {EmptyError} Delivers an EmptyError to the Observer's `error`
3513
* callback if the Observable completes before any `next` notification was sent.
36-
* @param {function} predicate - The condition any source emitted item has to satisfy.
14+
* @param {function} [predicate] - The condition any source emitted item has to satisfy.
15+
* @param {any} [defaultValue] - The default value to use if the predicate isn't
16+
* satisfied, or no values were emitted (if no predicate).
3717
* @return {Observable} An Observable that emits only the last item satisfying the given condition
3818
* from the source, or an NoSuchElementException if no such items are emitted.
3919
* @throws - Throws if no items that match the predicate are emitted by the source Observable.
4020
* @method last
4121
* @owner Observable
4222
*/
43-
export function last<T, R>(this: Observable<T>, predicate?: (value: T, index: number, source: Observable<T>) => boolean,
44-
resultSelector?: ((value: T, index: number) => R) | void,
45-
defaultValue?: R): Observable<T | R> {
46-
return higherOrder(predicate, resultSelector as any, defaultValue)(this);
23+
export function last<T>(this: Observable<T>, predicate?: (value: T, index: number, source: Observable<T>) => boolean,
24+
defaultValue?: T): Observable<T | T> {
25+
return higherOrder(predicate, defaultValue)(this);
4726
}

0 commit comments

Comments
 (0)