From 3c2cc0feace4c96c1e12c6dc549736b01cf4c30e Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 9 May 2019 19:06:44 -0700 Subject: [PATCH 1/8] feat(errors): Add new error checkingutilities Adds error checking utilities: , , , , . --- spec/index-spec.ts | 6 ++++ spec/util/errors-spec.ts | 11 +++++++ src/index.ts | 1 + src/internal/util/errors.ts | 62 +++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 spec/util/errors-spec.ts create mode 100644 src/internal/util/errors.ts diff --git a/spec/index-spec.ts b/spec/index-spec.ts index 0b469a36d1..d62e571cd0 100644 --- a/spec/index-spec.ts +++ b/spec/index-spec.ts @@ -46,6 +46,12 @@ describe('index', () => { expect(index.ObjectUnsubscribedError).to.exist; expect(index.UnsubscriptionError).to.exist; expect(index.TimeoutError).to.exist; + + expect(index.isOutOfRangeError).to.exist; + expect(index.isEmptyError).to.exist; + expect(index.isObjectUnsubscribedError).to.exist; + expect(index.isTeardownError).to.exist; + expect(index.isTimeoutError).to.exist; }); it('should export constants', () => { diff --git a/spec/util/errors-spec.ts b/spec/util/errors-spec.ts new file mode 100644 index 0000000000..a71c27fed6 --- /dev/null +++ b/spec/util/errors-spec.ts @@ -0,0 +1,11 @@ +import { expect } from 'chai'; +import { createRxError, RxErrorCode, isRxError } from 'rxjs/internal/util/errors'; + +describe('createRxError', () => { + it('should create an error with the Error constructor by default', () => { + const error = createRxError('message', RxErrorCode.Empty); + expect(error.message).to.equal('RxJS: message'); + expect(error.__rxjsErrorCode).to.equal(RxErrorCode.Empty); + expect(isRxError(error)).to.be.true; + }); +}); diff --git a/src/index.ts b/src/index.ts index 624a92be5c..dd220c7e1a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,6 +38,7 @@ export { EmptyError } from './internal/util/EmptyError'; export { ObjectUnsubscribedError } from './internal/util/ObjectUnsubscribedError'; export { UnsubscriptionError } from './internal/util/UnsubscriptionError'; export { TimeoutError } from './internal/util/TimeoutError'; +export { isRxError, isEmptyError, isObjectUnsubscribedError, isOutOfRangeError, isTeardownError, isTimeoutError } from './internal/util/errors'; /* Static observable creation exports */ export { bindCallback } from './internal/observable/bindCallback'; diff --git a/src/internal/util/errors.ts b/src/internal/util/errors.ts new file mode 100644 index 0000000000..1ec36a63a8 --- /dev/null +++ b/src/internal/util/errors.ts @@ -0,0 +1,62 @@ + +export const enum RxErrorCode { + Empty = 0, + OutOfRangeError = 1, + ObjectUnsubscribedError = 2, + TimeoutError = 3, + TeardownError = 4, +} + +export function createRxError(message: string, code: RxErrorCode, ErrorType: any = Error) { + const result = new ErrorType('RxJS: ' + message); + (result as any).__rxjsErrorCode = code; + return result; +} + +/** + * Tests to see if the error passed is an error that was created by RxJS internals. + */ +export function isRxError(err: any) { + return '__rxjsErrorCode' in err; +} + +/** + * Checks to see if the value passed is an error thrown by RxJS when an Observable + * sequence was empty. + */ +export function isEmptyError(err: any) { + return err.__rxjsErrorCode === RxErrorCode.Empty; +} + +/** + * Checks to see if the value passed is an error thrown by RxJS when an argument + * was out of range. + */ +export function isOutOfRangeError(err: any) { + return err.__rxjsErrorCode === RxErrorCode.OutOfRangeError; +} + +/** + * Checks to see if the value passed is an error thrown by RxJS when a Subject + * was unsubscribed, and an action was taken on it. + */ +export function isObjectUnsubscribedError(err: any) { + return err.__rxjsErrorCode === RxErrorCode.ObjectUnsubscribedError; +} + +/** + * Checks to see if the value passed is an error thrown by RxJS when an observable + * times out, for example with the {@link timeout} operator. + */ +export function isTimeoutError(err: any) { + return err.__rxjsErrorCode === RxErrorCode.TimeoutError; +} + +/** + * Checks to see if the value passed is an error that was thrown while executing + * teardown during either completion, error handling or unsubscription of the observable + * chain. + */ +export function isTeardownError(err: any) { + return err.__rxjsErrorCode === RxErrorCode.TeardownError; +} From 13906e015eb48e5c9d73aa739c2855ae51a82ffb Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 9 May 2019 19:08:44 -0700 Subject: [PATCH 2/8] refactor(ArgumentOutOfRangeError): is now deprecated BREAKING CHANGE: ArgumentOutOfRangeError message is now just `'out of range'`, use `isOutOfRangeError` util to identify. --- spec/operators/elementAt-spec.ts | 36 +++++++++++++------- spec/operators/skipLast-spec.ts | 11 ++++-- spec/operators/take-spec.ts | 11 ++++-- spec/operators/takeLast-spec.ts | 11 ++++-- spec/util/ArgumentOutOfRangeError-spec.ts | 2 +- src/internal/operators/elementAt.ts | 13 ++++--- src/internal/operators/skipLast.ts | 9 ++--- src/internal/operators/take.ts | 7 ++-- src/internal/operators/takeLast.ts | 7 ++-- src/internal/util/ArgumentOutOfRangeError.ts | 14 ++++++-- src/internal/util/EmptyError.ts | 6 +++- src/internal/util/ObjectUnsubscribedError.ts | 6 +++- src/internal/util/TimeoutError.ts | 4 +++ src/internal/util/UnsubscriptionError.ts | 8 ++++- 14 files changed, 104 insertions(+), 41 deletions(-) diff --git a/spec/operators/elementAt-spec.ts b/spec/operators/elementAt-spec.ts index 93228b40d5..bfe9d1cbc1 100644 --- a/spec/operators/elementAt-spec.ts +++ b/spec/operators/elementAt-spec.ts @@ -1,7 +1,8 @@ import { expect } from 'chai'; import { elementAt, mergeMap } from 'rxjs/operators'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; -import { ArgumentOutOfRangeError, of, range } from 'rxjs'; +import { ArgumentOutOfRangeError, of, range, isOutOfRangeError } from 'rxjs'; +import { createOutOfRangeError } from 'rxjs/internal/util/ArgumentOutOfRangeError'; declare function asDiagram(arg: string): Function; @@ -12,7 +13,7 @@ describe('elementAt operator', () => { const subs = '^ ! '; const expected = '--------(c|) '; - expectObservable((source).pipe(elementAt(2))).toBe(expected); + expectObservable(source.pipe(elementAt(2))).toBe(expected); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -21,7 +22,7 @@ describe('elementAt operator', () => { const subs = '^ !'; const expected = '--(a|)'; - expectObservable((source).pipe(elementAt(0))).toBe(expected); + expectObservable(source.pipe(elementAt(0))).toBe(expected); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -30,7 +31,7 @@ describe('elementAt operator', () => { const subs = '^ !'; const expected = '-----------(d|)'; - expectObservable((source).pipe(elementAt(3))).toBe(expected); + expectObservable(source.pipe(elementAt(3))).toBe(expected); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -39,7 +40,7 @@ describe('elementAt operator', () => { const subs = '^ !'; const expected = '--------(c|)'; - expectObservable((source).pipe(elementAt(2))).toBe(expected); + expectObservable(source.pipe(elementAt(2))).toBe(expected); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -48,7 +49,7 @@ describe('elementAt operator', () => { const subs = '(^!)'; const expected = '#'; - expectObservable((source).pipe(elementAt(0))).toBe(expected, undefined, new ArgumentOutOfRangeError()); + expectObservable(source.pipe(elementAt(0))).toBe(expected, undefined, new ArgumentOutOfRangeError()); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -57,7 +58,7 @@ describe('elementAt operator', () => { const subs = '(^!)'; const expected = '#'; - expectObservable((source).pipe(elementAt(0))).toBe(expected); + expectObservable(source.pipe(elementAt(0))).toBe(expected); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -66,7 +67,7 @@ describe('elementAt operator', () => { const subs = '^'; const expected = '-'; - expectObservable((source).pipe(elementAt(0))).toBe(expected); + expectObservable(source.pipe(elementAt(0))).toBe(expected); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -76,7 +77,7 @@ describe('elementAt operator', () => { const expected = '------- '; const unsub = ' ! '; - const result = (source).pipe(elementAt(2)); + const result = source.pipe(elementAt(2)); expectObservable(result, unsub).toBe(expected); expectSubscriptions(source.subscriptions).toBe(subs); @@ -88,7 +89,7 @@ describe('elementAt operator', () => { const expected = '------- '; const unsub = ' ! '; - const result = (source).pipe( + const result = source.pipe( mergeMap((x: any) => of(x)), elementAt(2), mergeMap((x: any) => of(x)) @@ -108,11 +109,22 @@ describe('elementAt operator', () => { const subs = '^ !'; const expected = '-----#'; - expectObservable((source).pipe(elementAt(3))) - .toBe(expected, null, new ArgumentOutOfRangeError()); + expectObservable(source.pipe(elementAt(3))) + .toBe(expected, null, createOutOfRangeError()); expectSubscriptions(source.subscriptions).toBe(subs); }); + it('should raise an out of range error if the index if out of range and there is no default value', () => { + of(0, 1, 2).pipe( + elementAt(3), + ) + .subscribe({ + error: err => { + expect(isOutOfRangeError(err)).to.be.true; + } + }); + }); + it('should return default value if index is out of range', () => { const source = hot('--a--|'); const subs = '^ !'; diff --git a/spec/operators/skipLast-spec.ts b/spec/operators/skipLast-spec.ts index 113606a930..88ea7bf5a7 100644 --- a/spec/operators/skipLast-spec.ts +++ b/spec/operators/skipLast-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { skipLast, mergeMap } from 'rxjs/operators'; -import { range, ArgumentOutOfRangeError, of } from 'rxjs'; +import { range, of, isOutOfRangeError } from 'rxjs'; declare function asDiagram(arg: string): Function; @@ -135,8 +135,13 @@ describe('skipLast operator', () => { }); it('should throw if total is less than zero', () => { - expect(() => { range(0, 10).pipe(skipLast(-1)); }) - .to.throw(ArgumentOutOfRangeError); + try { + range(0, 10).pipe(skipLast(-1)); + } catch (err) { + expect(isOutOfRangeError(err)).to.be.true; + return; + } + throw new Error('should not make it this far'); }); it('should not break unsubscription chain when unsubscribed explicitly', () => { diff --git a/spec/operators/take-spec.ts b/spec/operators/take-spec.ts index ea4e5aa1e6..4bb57ac087 100644 --- a/spec/operators/take-spec.ts +++ b/spec/operators/take-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { take, mergeMap } from 'rxjs/operators'; -import { range, ArgumentOutOfRangeError, of, Observable, Subject } from 'rxjs'; +import { range, of, Observable, Subject, isOutOfRangeError } from 'rxjs'; declare function asDiagram(arg: string): Function; @@ -108,8 +108,13 @@ describe('take operator', () => { }); it('should throw if total is less than zero', () => { - expect(() => { range(0, 10).pipe(take(-1)); }) - .to.throw(ArgumentOutOfRangeError); + try { + range(0, 10).pipe(take(-1)); + } catch (err) { + expect(isOutOfRangeError(err)).to.be.true; + return; + } + throw new Error('should not make it this far'); }); it('should not break unsubscription chain when unsubscribed explicitly', () => { diff --git a/spec/operators/takeLast-spec.ts b/spec/operators/takeLast-spec.ts index f689c5a6f2..76350e15c8 100644 --- a/spec/operators/takeLast-spec.ts +++ b/spec/operators/takeLast-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { takeLast, mergeMap } from 'rxjs/operators'; -import { range, ArgumentOutOfRangeError, of } from 'rxjs'; +import { range, of, isOutOfRangeError } from 'rxjs'; declare function asDiagram(arg: string): Function; @@ -142,8 +142,13 @@ describe('takeLast operator', () => { }); it('should throw if total is less than zero', () => { - expect(() => { range(0, 10).pipe(takeLast(-1)); }) - .to.throw(ArgumentOutOfRangeError); + try { + range(0, 10).pipe(takeLast(-1)); + } catch (err) { + expect(isOutOfRangeError(err)).to.be.true; + return; + } + throw new Error('should not reach this point'); }); it('should not break unsubscription chain when unsubscribed explicitly', () => { diff --git a/spec/util/ArgumentOutOfRangeError-spec.ts b/spec/util/ArgumentOutOfRangeError-spec.ts index 5d03960193..e46645a8f1 100644 --- a/spec/util/ArgumentOutOfRangeError-spec.ts +++ b/spec/util/ArgumentOutOfRangeError-spec.ts @@ -8,6 +8,6 @@ describe('ArgumentOutOfRangeError', () => { expect(error.name).to.be.equal('ArgumentOutOfRangeError'); }); it('Should have a message', () => { - expect(error.message).to.be.equal('argument out of range'); + expect(error.message).to.be.equal('out of range'); }); }); diff --git a/src/internal/operators/elementAt.ts b/src/internal/operators/elementAt.ts index 60af70dcd7..8c77f4d320 100644 --- a/src/internal/operators/elementAt.ts +++ b/src/internal/operators/elementAt.ts @@ -1,6 +1,10 @@ +<<<<<<< HEAD import { ArgumentOutOfRangeError } from '../util/ArgumentOutOfRangeError'; +======= +import { createOutOfRangeError } from '../util/ArgumentOutOfRangeError'; +>>>>>>> refactor(ArgumentOutOfRangeError): is now deprecated import { Observable } from '../Observable'; -import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; +import { MonoTypeOperatorFunction } from '../types'; import { filter } from './filter'; import { throwIfEmpty } from './throwIfEmpty'; import { defaultIfEmpty } from './defaultIfEmpty'; @@ -42,9 +46,10 @@ import { take } from './take'; * @see {@link single} * @see {@link take} * - * @throws {ArgumentOutOfRangeError} When using `elementAt(i)`, it delivers an + * @throws {Error} When using `elementAt(i)`, it delivers an * ArgumentOutOrRangeError to the Observer's `error` callback if `i < 0` or the * Observable has completed before emitting the i-th `next` notification. + * If you need to test for this, use {@link isOutOfRangeError}. * * @param {number} index Is the number `i` for the i-th source emission that has * happened since the subscription, starting from the number `0`. @@ -55,13 +60,13 @@ import { take } from './take'; * @owner Observable */ export function elementAt(index: number, defaultValue?: T): MonoTypeOperatorFunction { - if (index < 0) { throw new ArgumentOutOfRangeError(); } + if (index < 0) { throw createOutOfRangeError(); } const hasDefaultValue = arguments.length >= 2; return (source: Observable) => source.pipe( filter((v, i) => i === index), take(1), hasDefaultValue ? defaultIfEmpty(defaultValue) - : throwIfEmpty(() => new ArgumentOutOfRangeError()), + : throwIfEmpty(() => createOutOfRangeError()), ); } diff --git a/src/internal/operators/skipLast.ts b/src/internal/operators/skipLast.ts index 60b6087531..20a592d087 100644 --- a/src/internal/operators/skipLast.ts +++ b/src/internal/operators/skipLast.ts @@ -1,8 +1,8 @@ import { Operator } from '../Operator'; import { Subscriber } from '../Subscriber'; -import { ArgumentOutOfRangeError } from '../util/ArgumentOutOfRangeError'; import { Observable } from '../Observable'; import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; +import { createOutOfRangeError } from 'rxjs/internal/util/ArgumentOutOfRangeError'; /** * Skip the last `count` values emitted by the source Observable. @@ -33,8 +33,9 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * @see {@link skipWhile} * @see {@link take} * - * @throws {ArgumentOutOfRangeError} When using `skipLast(i)`, it throws - * ArgumentOutOrRangeError if `i < 0`. + * @throws {Error} When using `skipLast(i)`, it throws + * ArgumentOutOrRangeError if `i < 0`. If you need to test for this, use + * {@link isOutOfRangeError}. * * @param {number} count Number of elements to skip from the end of the source Observable. * @returns {Observable} An Observable that skips the last count values @@ -49,7 +50,7 @@ export function skipLast(count: number): MonoTypeOperatorFunction { class SkipLastOperator implements Operator { constructor(private _skipCount: number) { if (this._skipCount < 0) { - throw new ArgumentOutOfRangeError; + throw createOutOfRangeError(); } } diff --git a/src/internal/operators/take.ts b/src/internal/operators/take.ts index b43205c1cf..9d2844f538 100644 --- a/src/internal/operators/take.ts +++ b/src/internal/operators/take.ts @@ -1,6 +1,6 @@ import { Operator } from '../Operator'; import { Subscriber } from '../Subscriber'; -import { ArgumentOutOfRangeError } from '../util/ArgumentOutOfRangeError'; +import { createOutOfRangeError } from '../util/ArgumentOutOfRangeError'; import { empty } from '../observable/empty'; import { Observable } from '../Observable'; import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; @@ -41,8 +41,9 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * @see {@link takeWhile} * @see {@link skip} * - * @throws {ArgumentOutOfRangeError} When using `take(i)`, it delivers an + * @throws {Error} When using `take(i)`, it delivers an * ArgumentOutOrRangeError to the Observer's `error` callback if `i < 0`. + * If you need to test for this, use {@link isOutOfRangeError}. * * @param {number} count The maximum number of `next` values to emit. * @return {Observable} An Observable that emits only the first `count` @@ -64,7 +65,7 @@ export function take(count: number): MonoTypeOperatorFunction { class TakeOperator implements Operator { constructor(private total: number) { if (this.total < 0) { - throw new ArgumentOutOfRangeError; + throw createOutOfRangeError(); } } diff --git a/src/internal/operators/takeLast.ts b/src/internal/operators/takeLast.ts index 5d215f64fe..27b78017d9 100644 --- a/src/internal/operators/takeLast.ts +++ b/src/internal/operators/takeLast.ts @@ -1,6 +1,6 @@ import { Operator } from '../Operator'; import { Subscriber } from '../Subscriber'; -import { ArgumentOutOfRangeError } from '../util/ArgumentOutOfRangeError'; +import { createOutOfRangeError } from '../util/ArgumentOutOfRangeError'; import { empty } from '../observable/empty'; import { Observable } from '../Observable'; import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; @@ -37,8 +37,9 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * @see {@link takeWhile} * @see {@link skip} * - * @throws {ArgumentOutOfRangeError} When using `takeLast(i)`, it delivers an + * @throws {Error} When using `takeLast(i)`, it delivers an * ArgumentOutOrRangeError to the Observer's `error` callback if `i < 0`. + * If you need to test for this, use {@link isOutOfRangeError}. * * @param {number} count The maximum number of values to emit from the end of * the sequence of values emitted by the source Observable. @@ -60,7 +61,7 @@ export function takeLast(count: number): MonoTypeOperatorFunction { class TakeLastOperator implements Operator { constructor(private total: number) { if (this.total < 0) { - throw new ArgumentOutOfRangeError; + throw createOutOfRangeError(); } } diff --git a/src/internal/util/ArgumentOutOfRangeError.ts b/src/internal/util/ArgumentOutOfRangeError.ts index 949bfcc9da..46fc424bd9 100644 --- a/src/internal/util/ArgumentOutOfRangeError.ts +++ b/src/internal/util/ArgumentOutOfRangeError.ts @@ -1,3 +1,5 @@ +import { createRxError, RxErrorCode } from 'rxjs/internal/util/errors'; + export interface ArgumentOutOfRangeError extends Error { } @@ -7,7 +9,7 @@ export interface ArgumentOutOfRangeErrorCtor { function ArgumentOutOfRangeErrorImpl(this: any) { Error.call(this); - this.message = 'argument out of range'; + this.message = 'out of range'; this.name = 'ArgumentOutOfRangeError'; return this; } @@ -18,10 +20,18 @@ ArgumentOutOfRangeErrorImpl.prototype = Object.create(Error.prototype); * An error thrown when an element was queried at a certain index of an * Observable, but no such index or position exists in that sequence. * + * NOT INTENDED TO BE CREATED BY CONSUMING CODE. + * * @see {@link elementAt} * @see {@link take} * @see {@link takeLast} * * @class ArgumentOutOfRangeError + * + * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isOutOfRangeError} */ -export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = ArgumentOutOfRangeErrorImpl as any; \ No newline at end of file +export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = ArgumentOutOfRangeErrorImpl as any; + +export function createOutOfRangeError() { + return createRxError('out of range', RxErrorCode.OutOfRangeError, ArgumentOutOfRangeError); +} diff --git a/src/internal/util/EmptyError.ts b/src/internal/util/EmptyError.ts index 6dacfc6aa8..d578eba678 100644 --- a/src/internal/util/EmptyError.ts +++ b/src/internal/util/EmptyError.ts @@ -18,10 +18,14 @@ EmptyErrorImpl.prototype = Object.create(Error.prototype); * An error thrown when an Observable or a sequence was queried but has no * elements. * + * NOT INTENDED TO BE CREATED BY CONSUMING CODE. + * * @see {@link first} * @see {@link last} * @see {@link single} * * @class EmptyError + * + * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isEmptyError} */ -export const EmptyError: EmptyErrorCtor = EmptyErrorImpl as any; \ No newline at end of file +export const EmptyError: EmptyErrorCtor = EmptyErrorImpl as any; diff --git a/src/internal/util/ObjectUnsubscribedError.ts b/src/internal/util/ObjectUnsubscribedError.ts index e068c54347..89f8078c4f 100644 --- a/src/internal/util/ObjectUnsubscribedError.ts +++ b/src/internal/util/ObjectUnsubscribedError.ts @@ -18,9 +18,13 @@ ObjectUnsubscribedErrorImpl.prototype = Object.create(Error.prototype); * An error thrown when an action is invalid because the object has been * unsubscribed. * + * NOT INTENDED TO BE CREATED BY CONSUMING CODE. + * * @see {@link Subject} * @see {@link BehaviorSubject} * * @class ObjectUnsubscribedError + * + * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isOutOfRangeError} */ -export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = ObjectUnsubscribedErrorImpl as any; \ No newline at end of file +export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = ObjectUnsubscribedErrorImpl as any; diff --git a/src/internal/util/TimeoutError.ts b/src/internal/util/TimeoutError.ts index 3c654276a3..d75f23b1cc 100644 --- a/src/internal/util/TimeoutError.ts +++ b/src/internal/util/TimeoutError.ts @@ -17,8 +17,12 @@ TimeoutErrorImpl.prototype = Object.create(Error.prototype); /** * An error thrown when duetime elapses. * + * NOT INTENDED TO BE CREATED BY CONSUMING CODE. + * * @see {@link operators/timeout} * * @class TimeoutError + * + * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isTimeoutError} */ export const TimeoutError: TimeoutErrorCtor = TimeoutErrorImpl as any; diff --git a/src/internal/util/UnsubscriptionError.ts b/src/internal/util/UnsubscriptionError.ts index 293b9678a9..78ab6dd2bc 100644 --- a/src/internal/util/UnsubscriptionError.ts +++ b/src/internal/util/UnsubscriptionError.ts @@ -21,5 +21,11 @@ UnsubscriptionErrorImpl.prototype = Object.create(Error.prototype); /** * An error thrown when one or more errors have occurred during the * `unsubscribe` of a {@link Subscription}. + * + * NOT INTENDED TO BE CREATED BY CONSUMING CODE. + * + * @class UnsubscriptionError + * + * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isTeardownError} */ -export const UnsubscriptionError: UnsubscriptionErrorCtor = UnsubscriptionErrorImpl as any; \ No newline at end of file +export const UnsubscriptionError: UnsubscriptionErrorCtor = UnsubscriptionErrorImpl as any; From b0ac1e4e347d0348d67bd7c06fbcc76815fc3cfb Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 9 May 2019 19:15:37 -0700 Subject: [PATCH 3/8] refactor(EmptyError): is deprecated To test for empty errors, use `isEmptyError`. --- spec/operators/first-spec.ts | 7 ++++--- spec/operators/last-spec.ts | 7 ++++--- spec/operators/single-spec.ts | 7 ++++--- spec/operators/throwIfEmpty-spec.ts | 5 +++-- spec/util/EmptyError-spec.ts | 8 ++++++++ src/internal/operators/last.ts | 7 ++++++- src/internal/operators/single.ts | 10 ++++------ src/internal/operators/throwIfEmpty.ts | 8 ++------ src/internal/util/EmptyError.ts | 6 ++++++ 9 files changed, 41 insertions(+), 24 deletions(-) diff --git a/spec/operators/first-spec.ts b/spec/operators/first-spec.ts index dea1931cb6..1e837ed768 100644 --- a/spec/operators/first-spec.ts +++ b/spec/operators/first-spec.ts @@ -2,7 +2,8 @@ import { expect } from 'chai'; import { hot, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { first, mergeMap, delay } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; -import { of, from, Observable, Subject, EmptyError } from 'rxjs'; +import { of, from, Observable, Subject } from 'rxjs'; +import { createEmptyError } from 'rxjs/internal/util/EmptyError'; declare function asDiagram(arg: string): Function; @@ -33,7 +34,7 @@ describe('Observable.prototype.first', () => { const expected = '-----#'; const sub = '^ !'; - expectObservable(e1.pipe(first())).toBe(expected, null, new EmptyError()); + expectObservable(e1.pipe(first())).toBe(expected, null, createEmptyError()); expectSubscriptions(e1.subscriptions).toBe(sub); }); @@ -141,7 +142,7 @@ describe('Observable.prototype.first', () => { const expected = '---------------#'; const sub = '^ !'; - expectObservable(e1.pipe(first(x => x === 's'))).toBe(expected, null, new EmptyError()); + expectObservable(e1.pipe(first(x => x === 's'))).toBe(expected, null, createEmptyError()); expectSubscriptions(e1.subscriptions).toBe(sub); }); diff --git a/spec/operators/last-spec.ts b/spec/operators/last-spec.ts index 1cb84e72b9..3fb6c9afbd 100644 --- a/spec/operators/last-spec.ts +++ b/spec/operators/last-spec.ts @@ -1,7 +1,8 @@ import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { last, mergeMap } from 'rxjs/operators'; -import { EmptyError, of, from, Observable } from 'rxjs'; +import { of, from, Observable } from 'rxjs'; +import { createEmptyError } from 'rxjs/internal/util/EmptyError'; declare function asDiagram(arg: string): Function; @@ -21,7 +22,7 @@ describe('Observable.prototype.last', () => { const e1subs = '^ !'; const expected = '-----#'; - expectObservable(e1.pipe(last())).toBe(expected, null, new EmptyError()); + expectObservable(e1.pipe(last())).toBe(expected, null, createEmptyError()); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); @@ -30,7 +31,7 @@ describe('Observable.prototype.last', () => { const e1subs = '(^!)'; const expected = '#'; - expectObservable(e1.pipe(last())).toBe(expected, null, new EmptyError()); + expectObservable(e1.pipe(last())).toBe(expected, null, createEmptyError()); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); diff --git a/spec/operators/single-spec.ts b/spec/operators/single-spec.ts index 033abdb860..277d285c2d 100644 --- a/spec/operators/single-spec.ts +++ b/spec/operators/single-spec.ts @@ -1,7 +1,8 @@ import { expect } from 'chai'; import { hot, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { single, mergeMap, tap } from 'rxjs/operators'; -import { of, EmptyError } from 'rxjs'; +import { of } from 'rxjs'; +import { createEmptyError } from 'rxjs/internal/util/EmptyError'; declare function asDiagram(arg: string): Function; @@ -22,7 +23,7 @@ describe('single operator', () => { const e1subs = '^ !'; const expected = '---#'; - expectObservable(e1.pipe(single())).toBe(expected, null, new EmptyError()); + expectObservable(e1.pipe(single())).toBe(expected, null, createEmptyError()); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); @@ -134,7 +135,7 @@ describe('single operator', () => { return value === 'a'; }; - expectObservable(e1.pipe(single(predicate))).toBe(expected, null, new EmptyError()); + expectObservable(e1.pipe(single(predicate))).toBe(expected, null, createEmptyError()); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); diff --git a/spec/operators/throwIfEmpty-spec.ts b/spec/operators/throwIfEmpty-spec.ts index 44304d7847..bc7ab08770 100644 --- a/spec/operators/throwIfEmpty-spec.ts +++ b/spec/operators/throwIfEmpty-spec.ts @@ -1,7 +1,8 @@ import { expect } from 'chai'; -import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; +import { cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { EMPTY, of, EmptyError, defer, throwError } from 'rxjs'; import { throwIfEmpty, mergeMap, retry } from 'rxjs/operators'; +import { createEmptyError } from 'rxjs/internal/util/EmptyError'; declare function asDiagram(arg: string): Function; @@ -169,7 +170,7 @@ describe('throwIfEmpty', () => { const expected = '----#'; expectObservable( source.pipe(throwIfEmpty()) - ).toBe(expected, undefined, new EmptyError()); + ).toBe(expected, undefined, createEmptyError()); expectSubscriptions(source.subscriptions).toBe([sub1]); }); diff --git a/spec/util/EmptyError-spec.ts b/spec/util/EmptyError-spec.ts index 311836cefa..10fc716269 100644 --- a/spec/util/EmptyError-spec.ts +++ b/spec/util/EmptyError-spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { EmptyError } from 'rxjs/util/EmptyError'; +import { createEmptyError } from 'rxjs/internal/util/EmptyError'; /** @test {EmptyError} */ describe('EmptyError', () => { @@ -11,3 +12,10 @@ describe('EmptyError', () => { expect(error.message).to.be.equal('no elements in sequence'); }); }); + +describe('createEmptyError', () => { + const error = createEmptyError(); + it('Should have a message', () => { + expect(error.message).to.be.equal('no elements in sequence'); + }); +}); diff --git a/src/internal/operators/last.ts b/src/internal/operators/last.ts index 41b3e9332c..2dc2fdacfa 100644 --- a/src/internal/operators/last.ts +++ b/src/internal/operators/last.ts @@ -1,5 +1,9 @@ import { Observable } from '../Observable'; +<<<<<<< HEAD import { EmptyError } from '../util/EmptyError'; +======= +import { createEmptyError } from '../util/EmptyError'; +>>>>>>> refactor(EmptyError): is deprecated import { OperatorFunction } from '../../internal/types'; import { filter } from './filter'; import { takeLast } from './takeLast'; @@ -38,6 +42,7 @@ export function last( * @return {Observable} An Observable that emits only the last item satisfying the given condition * from the source, or an NoSuchElementException if no such items are emitted. * @throws - Throws if no items that match the predicate are emitted by the source Observable. + * You can check for this with {@link isEmptyError}. */ export function last( predicate?: ((value: T, index: number, source: Observable) => boolean) | null, @@ -47,6 +52,6 @@ export function last( return (source: Observable) => source.pipe( predicate ? filter((v, i) => predicate(v, i, source)) : identity, takeLast(1), - hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(() => new EmptyError()), + hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(createEmptyError), ); } diff --git a/src/internal/operators/single.ts b/src/internal/operators/single.ts index e23e1473bc..71be2f61cf 100644 --- a/src/internal/operators/single.ts +++ b/src/internal/operators/single.ts @@ -1,7 +1,7 @@ import { Observable } from '../Observable'; import { Operator } from '../Operator'; import { Subscriber } from '../Subscriber'; -import { EmptyError } from '../util/EmptyError'; +import { EmptyError, createEmptyError } from '../util/EmptyError'; import { Observer, MonoTypeOperatorFunction, TeardownLogic } from '../types'; @@ -42,14 +42,12 @@ import { Observer, MonoTypeOperatorFunction, TeardownLogic } from '../types'; * @see {@link findIndex} * @see {@link elementAt} * - * @throws {EmptyError} Delivers an EmptyError to the Observer's `error` + * @throws {Error} Delivers an EmptyError to the Observer's `error` * callback if the Observable completes before any `next` notification was sent. + * To test for this, use {@link isEmptyError}. * @param {Function} predicate - A predicate function to evaluate items emitted by the source Observable. * @return {Observable} An Observable that emits the single item emitted by the source Observable that matches * the predicate or `undefined` when no items match. - * - * @method single - * @owner Observable */ export function single(predicate?: (value: T, index: number, source: Observable) => boolean): MonoTypeOperatorFunction { return (source: Observable) => source.lift(new SingleOperator(predicate, source)); @@ -117,7 +115,7 @@ class SingleSubscriber extends Subscriber { destination.next(this.seenValue ? this.singleValue : undefined); destination.complete(); } else { - destination.error(new EmptyError); + destination.error(createEmptyError()); } } } diff --git a/src/internal/operators/throwIfEmpty.ts b/src/internal/operators/throwIfEmpty.ts index 7f39c4e93f..73778b23b6 100644 --- a/src/internal/operators/throwIfEmpty.ts +++ b/src/internal/operators/throwIfEmpty.ts @@ -1,4 +1,4 @@ -import { EmptyError } from '../util/EmptyError'; +import { EmptyError, createEmptyError } from '../util/EmptyError'; import { Observable } from '../Observable'; import { Operator } from '../Operator'; import { Subscriber } from '../Subscriber'; @@ -34,7 +34,7 @@ import { TeardownLogic, MonoTypeOperatorFunction } from '../types'; * error to be thrown when the source observable completes without emitting a * value. */ -export function throwIfEmpty (errorFactory: (() => any) = defaultErrorFactory): MonoTypeOperatorFunction { +export function throwIfEmpty (errorFactory: (() => any) = createEmptyError): MonoTypeOperatorFunction { return (source: Observable) => { return source.lift(new ThrowIfEmptyOperator(errorFactory)); }; @@ -75,7 +75,3 @@ class ThrowIfEmptySubscriber extends Subscriber { } } } - -function defaultErrorFactory() { - return new EmptyError(); -} diff --git a/src/internal/util/EmptyError.ts b/src/internal/util/EmptyError.ts index d578eba678..3a30ab6726 100644 --- a/src/internal/util/EmptyError.ts +++ b/src/internal/util/EmptyError.ts @@ -1,3 +1,5 @@ +import { createRxError, RxErrorCode } from 'rxjs/internal/util/errors'; + export interface EmptyError extends Error { } @@ -29,3 +31,7 @@ EmptyErrorImpl.prototype = Object.create(Error.prototype); * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isEmptyError} */ export const EmptyError: EmptyErrorCtor = EmptyErrorImpl as any; + +export function createEmptyError() { + return createRxError('no elements in sequence', RxErrorCode.Empty, EmptyError); +} From 8e0df4349d254e85757c873dc588032a7d6d33a1 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 9 May 2019 19:19:15 -0700 Subject: [PATCH 4/8] refactor(ObjectUnsubscribedError): is deprecated Use `isObjectUnsubscribedError` to test for this error type. --- spec/util/ObjectUnsubscribedError-spec.ts | 8 ++++++++ src/internal/BehaviorSubject.ts | 4 ++-- src/internal/ReplaySubject.ts | 4 ++-- src/internal/Subject.ts | 12 ++++++------ src/internal/util/ObjectUnsubscribedError.ts | 6 ++++++ 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/spec/util/ObjectUnsubscribedError-spec.ts b/spec/util/ObjectUnsubscribedError-spec.ts index 8bd17f043b..ebd2c1d9af 100644 --- a/spec/util/ObjectUnsubscribedError-spec.ts +++ b/spec/util/ObjectUnsubscribedError-spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { ObjectUnsubscribedError } from 'rxjs/util/ObjectUnsubscribedError'; +import { createObjectUnsubscribedError } from 'rxjs/internal/util/ObjectUnsubscribedError'; /** @test {ObjectUnsubscribedError} */ describe('ObjectUnsubscribedError', () => { @@ -11,3 +12,10 @@ describe('ObjectUnsubscribedError', () => { expect(error.message).to.be.equal('object unsubscribed'); }); }); + +describe('createObjectUnsubscribedError', () => { + const error = createObjectUnsubscribedError(); + it('Should have a message', () => { + expect(error.message).to.be.equal('object unsubscribed'); + }); +}); diff --git a/src/internal/BehaviorSubject.ts b/src/internal/BehaviorSubject.ts index 20de21c668..179bffddab 100644 --- a/src/internal/BehaviorSubject.ts +++ b/src/internal/BehaviorSubject.ts @@ -2,7 +2,7 @@ import { Subject } from './Subject'; import { Subscriber } from './Subscriber'; import { Subscription } from './Subscription'; import { SubscriptionLike } from './types'; -import { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError'; +import { createObjectUnsubscribedError } from './util/ObjectUnsubscribedError'; /** * A variant of Subject that requires an initial value and emits its current @@ -33,7 +33,7 @@ export class BehaviorSubject extends Subject { if (this.hasError) { throw this.thrownError; } else if (this.closed) { - throw new ObjectUnsubscribedError(); + throw createObjectUnsubscribedError(); } else { return this._value; } diff --git a/src/internal/ReplaySubject.ts b/src/internal/ReplaySubject.ts index d6d8eb7dfd..bef7e4637d 100644 --- a/src/internal/ReplaySubject.ts +++ b/src/internal/ReplaySubject.ts @@ -4,7 +4,7 @@ import { queue } from './scheduler/queue'; import { Subscriber } from './Subscriber'; import { Subscription } from './Subscription'; import { ObserveOnSubscriber } from './operators/observeOn'; -import { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError'; +import { createObjectUnsubscribedError } from './util/ObjectUnsubscribedError'; import { SubjectSubscription } from './SubjectSubscription'; /** * A variant of Subject that "replays" or emits old values to new subscribers. @@ -63,7 +63,7 @@ export class ReplaySubject extends Subject { let subscription: Subscription; if (this.closed) { - throw new ObjectUnsubscribedError(); + throw createObjectUnsubscribedError(); } else if (this.isStopped || this.hasError) { subscription = Subscription.EMPTY; } else { diff --git a/src/internal/Subject.ts b/src/internal/Subject.ts index d5b473436f..c0a7703105 100644 --- a/src/internal/Subject.ts +++ b/src/internal/Subject.ts @@ -3,7 +3,7 @@ import { Observable } from './Observable'; import { Subscriber } from './Subscriber'; import { Subscription } from './Subscription'; import { Observer, SubscriptionLike, TeardownLogic } from './types'; -import { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError'; +import { createObjectUnsubscribedError } from './util/ObjectUnsubscribedError'; import { SubjectSubscription } from './SubjectSubscription'; import { rxSubscriber as rxSubscriberSymbol } from '../internal/symbol/rxSubscriber'; @@ -60,7 +60,7 @@ export class Subject extends Observable implements SubscriptionLike { next(value?: T) { if (this.closed) { - throw new ObjectUnsubscribedError(); + throw createObjectUnsubscribedError(); } if (!this.isStopped) { const { observers } = this; @@ -74,7 +74,7 @@ export class Subject extends Observable implements SubscriptionLike { error(err: any) { if (this.closed) { - throw new ObjectUnsubscribedError(); + throw createObjectUnsubscribedError(); } this.hasError = true; this.thrownError = err; @@ -90,7 +90,7 @@ export class Subject extends Observable implements SubscriptionLike { complete() { if (this.closed) { - throw new ObjectUnsubscribedError(); + throw createObjectUnsubscribedError(); } this.isStopped = true; const { observers } = this; @@ -111,7 +111,7 @@ export class Subject extends Observable implements SubscriptionLike { /** @deprecated This is an internal implementation detail, do not use. */ _trySubscribe(subscriber: Subscriber): TeardownLogic { if (this.closed) { - throw new ObjectUnsubscribedError(); + throw createObjectUnsubscribedError(); } else { return super._trySubscribe(subscriber); } @@ -120,7 +120,7 @@ export class Subject extends Observable implements SubscriptionLike { /** @deprecated This is an internal implementation detail, do not use. */ _subscribe(subscriber: Subscriber): Subscription { if (this.closed) { - throw new ObjectUnsubscribedError(); + throw createObjectUnsubscribedError(); } else if (this.hasError) { subscriber.error(this.thrownError); return Subscription.EMPTY; diff --git a/src/internal/util/ObjectUnsubscribedError.ts b/src/internal/util/ObjectUnsubscribedError.ts index 89f8078c4f..6ea14a6543 100644 --- a/src/internal/util/ObjectUnsubscribedError.ts +++ b/src/internal/util/ObjectUnsubscribedError.ts @@ -1,3 +1,5 @@ +import { createRxError, RxErrorCode } from 'rxjs/internal/util/errors'; + export interface ObjectUnsubscribedError extends Error { } @@ -28,3 +30,7 @@ ObjectUnsubscribedErrorImpl.prototype = Object.create(Error.prototype); * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isOutOfRangeError} */ export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = ObjectUnsubscribedErrorImpl as any; + +export function createObjectUnsubscribedError() { + return createRxError('object unsubscribed', RxErrorCode.ObjectUnsubscribedError, ObjectUnsubscribedError); +} From 0ec9588f7eb695b175fa157ff37fe0309256c059 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 9 May 2019 19:24:21 -0700 Subject: [PATCH 5/8] feat(TimeoutError): is deprecated Use `isTimeoutError` to test. BREAKING CHANGE: TimeoutError message is now `observable timed out`. --- spec/operators/timeout-spec.ts | 3 ++- spec/util/TimeoutError-spec.ts | 10 +++++++++- src/internal/operators/timeout.ts | 7 ++++++- src/internal/util/TimeoutError.ts | 8 +++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/spec/operators/timeout-spec.ts b/spec/operators/timeout-spec.ts index c49c71c31c..664c5051d6 100644 --- a/spec/operators/timeout-spec.ts +++ b/spec/operators/timeout-spec.ts @@ -3,13 +3,14 @@ import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/mar import { timeout, mergeMap } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; import { TimeoutError, of } from 'rxjs'; +import { createTimeoutError } from 'rxjs/internal/util/TimeoutError'; declare function asDiagram(arg: string): Function; declare const rxTestScheduler: TestScheduler; /** @test {timeout} */ describe('timeout operator', () => { - const defaultTimeoutError = new TimeoutError(); + const defaultTimeoutError = createTimeoutError(); asDiagram('timeout(50)')('should timeout after a specified timeout period', () => { const e1 = cold('-------a--b--|'); diff --git a/spec/util/TimeoutError-spec.ts b/spec/util/TimeoutError-spec.ts index 5f7d3edd9f..ed861c8121 100644 --- a/spec/util/TimeoutError-spec.ts +++ b/spec/util/TimeoutError-spec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { TimeoutError } from 'rxjs/util/TimeoutError'; +import { createTimeoutError } from 'rxjs/internal/util/TimeoutError'; /** @test {TimeoutError} */ describe('TimeoutError', () => { @@ -8,6 +9,13 @@ describe('TimeoutError', () => { expect(error.name).to.be.equal('TimeoutError'); }); it('Should have a message', () => { - expect(error.message).to.be.equal('Timeout has occurred'); + expect(error.message).to.be.equal('observable timed out'); + }); +}); + +describe('TimeoutError', () => { + const error = createTimeoutError(); + it('Should have a message', () => { + expect(error.message).to.be.equal('observable timed out'); }); }); diff --git a/src/internal/operators/timeout.ts b/src/internal/operators/timeout.ts index 34718d8ac9..e62f04e3f5 100644 --- a/src/internal/operators/timeout.ts +++ b/src/internal/operators/timeout.ts @@ -1,6 +1,11 @@ import { async } from '../scheduler/async'; +<<<<<<< HEAD import { TimeoutError } from '../util/TimeoutError'; import { MonoTypeOperatorFunction, SchedulerAction, SchedulerLike, TeardownLogic } from '../types'; +======= +import { createTimeoutError } from '../util/TimeoutError'; +import { MonoTypeOperatorFunction, SchedulerLike } from '../types'; +>>>>>>> feat(TimeoutError): is deprecated import { timeoutWith } from './timeoutWith'; import { throwError } from '../observable/throwError'; @@ -84,5 +89,5 @@ import { throwError } from '../observable/throwError'; */ export function timeout(due: number | Date, scheduler: SchedulerLike = async): MonoTypeOperatorFunction { - return timeoutWith(due, throwError(new TimeoutError()), scheduler); + return timeoutWith(due, throwError(createTimeoutError()), scheduler); } diff --git a/src/internal/util/TimeoutError.ts b/src/internal/util/TimeoutError.ts index d75f23b1cc..e830bdfa68 100644 --- a/src/internal/util/TimeoutError.ts +++ b/src/internal/util/TimeoutError.ts @@ -1,3 +1,5 @@ +import { createRxError, RxErrorCode } from 'rxjs/internal/util/errors'; + export interface TimeoutError extends Error { } @@ -7,7 +9,7 @@ export interface TimeoutErrorCtor { function TimeoutErrorImpl(this: any) { Error.call(this); - this.message = 'Timeout has occurred'; + this.message = 'observable timed out'; this.name = 'TimeoutError'; return this; } @@ -26,3 +28,7 @@ TimeoutErrorImpl.prototype = Object.create(Error.prototype); * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isTimeoutError} */ export const TimeoutError: TimeoutErrorCtor = TimeoutErrorImpl as any; + +export function createTimeoutError() { + return createRxError('observable timed out', RxErrorCode.TimeoutError, TimeoutError); +} From 7f3d89ecee99db1f220409c9c953b6539431e8e1 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 9 May 2019 19:31:04 -0700 Subject: [PATCH 6/8] feat(UnsubscriptionError): is deprecated To test for errors that have occurred during teardown, use `isTeardownError` --- spec/util/UnsubscriptionError-spec.ts | 4 ++-- src/internal/Subscription.ts | 11 ++++++----- src/internal/util/UnsubscriptionError.ts | 8 ++++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/spec/util/UnsubscriptionError-spec.ts b/spec/util/UnsubscriptionError-spec.ts index 2799d680f5..2e3e5f8818 100644 --- a/spec/util/UnsubscriptionError-spec.ts +++ b/spec/util/UnsubscriptionError-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { UnsubscriptionError, Observable, timer, merge } from 'rxjs'; +import { UnsubscriptionError, Observable, timer, merge, isTeardownError } from 'rxjs'; /** @test {UnsubscriptionError} */ describe('UnsubscriptionError', () => { @@ -16,7 +16,7 @@ describe('UnsubscriptionError', () => { try { subscription.unsubscribe(); } catch (err) { - expect(err instanceof UnsubscriptionError).to.equal(true); + expect(isTeardownError(err)).to.equal(true); expect(err.errors).to.deep.equal([err1, err2]); expect(err.name).to.equal('UnsubscriptionError'); } diff --git a/src/internal/Subscription.ts b/src/internal/Subscription.ts index 1a07ffa508..ebe991630e 100644 --- a/src/internal/Subscription.ts +++ b/src/internal/Subscription.ts @@ -1,8 +1,9 @@ import { isArray } from './util/isArray'; import { isObject } from './util/isObject'; import { isFunction } from './util/isFunction'; -import { UnsubscriptionError } from './util/UnsubscriptionError'; +import { UnsubscriptionError, createTeardownError } from './util/UnsubscriptionError'; import { SubscriptionLike, TeardownLogic } from './types'; +import { isTeardownError } from 'rxjs/internal/util/errors'; /** * Represents a disposable resource, such as the execution of an Observable. A @@ -78,7 +79,7 @@ export class Subscription implements SubscriptionLike { try { _unsubscribe.call(this); } catch (e) { - errors = e instanceof UnsubscriptionError ? flattenUnsubscriptionErrors(e.errors) : [e]; + errors = isTeardownError(e) ? flattenUnsubscriptionErrors(e.errors) : [e]; } } @@ -93,7 +94,7 @@ export class Subscription implements SubscriptionLike { sub.unsubscribe(); } catch (e) { errors = errors || []; - if (e instanceof UnsubscriptionError) { + if (isTeardownError(e)) { errors = errors.concat(flattenUnsubscriptionErrors(e.errors)); } else { errors.push(e); @@ -104,7 +105,7 @@ export class Subscription implements SubscriptionLike { } if (errors) { - throw new UnsubscriptionError(errors); + throw createTeardownError(errors); } } @@ -207,5 +208,5 @@ export class Subscription implements SubscriptionLike { } function flattenUnsubscriptionErrors(errors: any[]) { - return errors.reduce((errs, err) => errs.concat((err instanceof UnsubscriptionError) ? err.errors : err), []); + return errors.reduce((errs, err) => errs.concat((isTeardownError(err)) ? err.errors : err), []); } diff --git a/src/internal/util/UnsubscriptionError.ts b/src/internal/util/UnsubscriptionError.ts index 78ab6dd2bc..7f808116b0 100644 --- a/src/internal/util/UnsubscriptionError.ts +++ b/src/internal/util/UnsubscriptionError.ts @@ -1,3 +1,5 @@ +import { RxErrorCode } from 'rxjs/internal/util/errors'; + export interface UnsubscriptionError extends Error { readonly errors: any[]; } @@ -29,3 +31,9 @@ UnsubscriptionErrorImpl.prototype = Object.create(Error.prototype); * @deprecated (gone in v8) for `instanceof` checks, instead use {@link isTeardownError} */ export const UnsubscriptionError: UnsubscriptionErrorCtor = UnsubscriptionErrorImpl as any; + +export function createTeardownError(errors: any[]) { + const error: any = new UnsubscriptionError(errors); + error.__rxjsErrorCode = RxErrorCode.TeardownError; + return error; +} From fabd61740784735357ad4bc5120f65ae7f59fbf7 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 9 May 2019 19:33:05 -0700 Subject: [PATCH 7/8] refactor: renamed items in RxErrorCode internal refactor --- src/internal/operators/elementAt.ts | 6 +----- src/internal/operators/last.ts | 4 ---- src/internal/util/ArgumentOutOfRangeError.ts | 2 +- src/internal/util/ObjectUnsubscribedError.ts | 2 +- src/internal/util/TimeoutError.ts | 2 +- src/internal/util/UnsubscriptionError.ts | 2 +- src/internal/util/errors.ts | 22 +++++++++++++------- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/internal/operators/elementAt.ts b/src/internal/operators/elementAt.ts index 8c77f4d320..c50d5ff4b7 100644 --- a/src/internal/operators/elementAt.ts +++ b/src/internal/operators/elementAt.ts @@ -1,14 +1,10 @@ -<<<<<<< HEAD -import { ArgumentOutOfRangeError } from '../util/ArgumentOutOfRangeError'; -======= import { createOutOfRangeError } from '../util/ArgumentOutOfRangeError'; ->>>>>>> refactor(ArgumentOutOfRangeError): is now deprecated import { Observable } from '../Observable'; import { MonoTypeOperatorFunction } from '../types'; import { filter } from './filter'; import { throwIfEmpty } from './throwIfEmpty'; import { defaultIfEmpty } from './defaultIfEmpty'; -import { take } from './take'; +import { take } from './take';s /** * Emits the single value at the specified `index` in a sequence of emissions diff --git a/src/internal/operators/last.ts b/src/internal/operators/last.ts index 2dc2fdacfa..0e2698baaf 100644 --- a/src/internal/operators/last.ts +++ b/src/internal/operators/last.ts @@ -1,9 +1,5 @@ import { Observable } from '../Observable'; -<<<<<<< HEAD -import { EmptyError } from '../util/EmptyError'; -======= import { createEmptyError } from '../util/EmptyError'; ->>>>>>> refactor(EmptyError): is deprecated import { OperatorFunction } from '../../internal/types'; import { filter } from './filter'; import { takeLast } from './takeLast'; diff --git a/src/internal/util/ArgumentOutOfRangeError.ts b/src/internal/util/ArgumentOutOfRangeError.ts index 46fc424bd9..23f668e72e 100644 --- a/src/internal/util/ArgumentOutOfRangeError.ts +++ b/src/internal/util/ArgumentOutOfRangeError.ts @@ -33,5 +33,5 @@ ArgumentOutOfRangeErrorImpl.prototype = Object.create(Error.prototype); export const ArgumentOutOfRangeError: ArgumentOutOfRangeErrorCtor = ArgumentOutOfRangeErrorImpl as any; export function createOutOfRangeError() { - return createRxError('out of range', RxErrorCode.OutOfRangeError, ArgumentOutOfRangeError); + return createRxError('out of range', RxErrorCode.OutOfRange, ArgumentOutOfRangeError); } diff --git a/src/internal/util/ObjectUnsubscribedError.ts b/src/internal/util/ObjectUnsubscribedError.ts index 6ea14a6543..4fb1b03d87 100644 --- a/src/internal/util/ObjectUnsubscribedError.ts +++ b/src/internal/util/ObjectUnsubscribedError.ts @@ -32,5 +32,5 @@ ObjectUnsubscribedErrorImpl.prototype = Object.create(Error.prototype); export const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = ObjectUnsubscribedErrorImpl as any; export function createObjectUnsubscribedError() { - return createRxError('object unsubscribed', RxErrorCode.ObjectUnsubscribedError, ObjectUnsubscribedError); + return createRxError('object unsubscribed', RxErrorCode.ObjectUnsubscribed, ObjectUnsubscribedError); } diff --git a/src/internal/util/TimeoutError.ts b/src/internal/util/TimeoutError.ts index e830bdfa68..b9e403ef0f 100644 --- a/src/internal/util/TimeoutError.ts +++ b/src/internal/util/TimeoutError.ts @@ -30,5 +30,5 @@ TimeoutErrorImpl.prototype = Object.create(Error.prototype); export const TimeoutError: TimeoutErrorCtor = TimeoutErrorImpl as any; export function createTimeoutError() { - return createRxError('observable timed out', RxErrorCode.TimeoutError, TimeoutError); + return createRxError('observable timed out', RxErrorCode.Timeout, TimeoutError); } diff --git a/src/internal/util/UnsubscriptionError.ts b/src/internal/util/UnsubscriptionError.ts index 7f808116b0..96e4353a9d 100644 --- a/src/internal/util/UnsubscriptionError.ts +++ b/src/internal/util/UnsubscriptionError.ts @@ -34,6 +34,6 @@ export const UnsubscriptionError: UnsubscriptionErrorCtor = UnsubscriptionErrorI export function createTeardownError(errors: any[]) { const error: any = new UnsubscriptionError(errors); - error.__rxjsErrorCode = RxErrorCode.TeardownError; + error.__rxjsErrorCode = RxErrorCode.Teardown; return error; } diff --git a/src/internal/util/errors.ts b/src/internal/util/errors.ts index 1ec36a63a8..96d8f7a1b0 100644 --- a/src/internal/util/errors.ts +++ b/src/internal/util/errors.ts @@ -1,12 +1,18 @@ +/** + * Used to identify different types of RxJS errors. + */ export const enum RxErrorCode { Empty = 0, - OutOfRangeError = 1, - ObjectUnsubscribedError = 2, - TimeoutError = 3, - TeardownError = 4, + OutOfRange = 1, + ObjectUnsubscribed = 2, + Timeout = 3, + Teardown = 4, } +/** + * Creates an error and decorates it with the appropriate error code for identification later. + */ export function createRxError(message: string, code: RxErrorCode, ErrorType: any = Error) { const result = new ErrorType('RxJS: ' + message); (result as any).__rxjsErrorCode = code; @@ -33,7 +39,7 @@ export function isEmptyError(err: any) { * was out of range. */ export function isOutOfRangeError(err: any) { - return err.__rxjsErrorCode === RxErrorCode.OutOfRangeError; + return err.__rxjsErrorCode === RxErrorCode.OutOfRange; } /** @@ -41,7 +47,7 @@ export function isOutOfRangeError(err: any) { * was unsubscribed, and an action was taken on it. */ export function isObjectUnsubscribedError(err: any) { - return err.__rxjsErrorCode === RxErrorCode.ObjectUnsubscribedError; + return err.__rxjsErrorCode === RxErrorCode.ObjectUnsubscribed; } /** @@ -49,7 +55,7 @@ export function isObjectUnsubscribedError(err: any) { * times out, for example with the {@link timeout} operator. */ export function isTimeoutError(err: any) { - return err.__rxjsErrorCode === RxErrorCode.TimeoutError; + return err.__rxjsErrorCode === RxErrorCode.Timeout; } /** @@ -58,5 +64,5 @@ export function isTimeoutError(err: any) { * chain. */ export function isTeardownError(err: any) { - return err.__rxjsErrorCode === RxErrorCode.TeardownError; + return err.__rxjsErrorCode === RxErrorCode.Teardown; } From ffa51c18f473eb838249af506e9974f221c1ca85 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Wed, 15 May 2019 10:52:42 -0700 Subject: [PATCH 8/8] fixup! refactor: renamed items in RxErrorCode --- spec/operators/elementAt-spec.ts | 13 +++++++++---- spec/util/UnsubscriptionError-spec.ts | 5 ++--- src/internal/Subscription.ts | 2 +- src/internal/operators/elementAt.ts | 2 +- src/internal/operators/timeout.ts | 5 ----- src/internal/util/UnsubscriptionError.ts | 1 + 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/spec/operators/elementAt-spec.ts b/spec/operators/elementAt-spec.ts index bfe9d1cbc1..83e6cf58b3 100644 --- a/spec/operators/elementAt-spec.ts +++ b/spec/operators/elementAt-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { elementAt, mergeMap } from 'rxjs/operators'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; -import { ArgumentOutOfRangeError, of, range, isOutOfRangeError } from 'rxjs'; +import { of, range, isOutOfRangeError } from 'rxjs'; import { createOutOfRangeError } from 'rxjs/internal/util/ArgumentOutOfRangeError'; declare function asDiagram(arg: string): Function; @@ -49,7 +49,7 @@ describe('elementAt operator', () => { const subs = '(^!)'; const expected = '#'; - expectObservable(source.pipe(elementAt(0))).toBe(expected, undefined, new ArgumentOutOfRangeError()); + expectObservable(source.pipe(elementAt(0))).toBe(expected, undefined, createOutOfRangeError()); expectSubscriptions(source.subscriptions).toBe(subs); }); @@ -100,8 +100,13 @@ describe('elementAt operator', () => { }); it('should throw if index is smaller than zero', () => { - expect(() => { range(0, 10).pipe(elementAt(-1)); }) - .to.throw(ArgumentOutOfRangeError); + try { + range(0, 10).pipe(elementAt(-1)); + } catch (err) { + expect(isOutOfRangeError(err)).to.be.true; + return; + } + expect('').to.equal('it should not get here'); }); it('should raise error if index is out of range but does not have default value', () => { diff --git a/spec/util/UnsubscriptionError-spec.ts b/spec/util/UnsubscriptionError-spec.ts index 2e3e5f8818..59514420ab 100644 --- a/spec/util/UnsubscriptionError-spec.ts +++ b/spec/util/UnsubscriptionError-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { UnsubscriptionError, Observable, timer, merge, isTeardownError } from 'rxjs'; +import { Observable, timer, merge, isTeardownError } from 'rxjs'; /** @test {UnsubscriptionError} */ describe('UnsubscriptionError', () => { @@ -16,9 +16,8 @@ describe('UnsubscriptionError', () => { try { subscription.unsubscribe(); } catch (err) { - expect(isTeardownError(err)).to.equal(true); + expect(isTeardownError(err)).to.be.true; expect(err.errors).to.deep.equal([err1, err2]); - expect(err.name).to.equal('UnsubscriptionError'); } }); }); diff --git a/src/internal/Subscription.ts b/src/internal/Subscription.ts index ebe991630e..1e7fc90332 100644 --- a/src/internal/Subscription.ts +++ b/src/internal/Subscription.ts @@ -1,7 +1,7 @@ import { isArray } from './util/isArray'; import { isObject } from './util/isObject'; import { isFunction } from './util/isFunction'; -import { UnsubscriptionError, createTeardownError } from './util/UnsubscriptionError'; +import { createTeardownError } from './util/UnsubscriptionError'; import { SubscriptionLike, TeardownLogic } from './types'; import { isTeardownError } from 'rxjs/internal/util/errors'; diff --git a/src/internal/operators/elementAt.ts b/src/internal/operators/elementAt.ts index c50d5ff4b7..bcd5617eed 100644 --- a/src/internal/operators/elementAt.ts +++ b/src/internal/operators/elementAt.ts @@ -4,7 +4,7 @@ import { MonoTypeOperatorFunction } from '../types'; import { filter } from './filter'; import { throwIfEmpty } from './throwIfEmpty'; import { defaultIfEmpty } from './defaultIfEmpty'; -import { take } from './take';s +import { take } from './take'; /** * Emits the single value at the specified `index` in a sequence of emissions diff --git a/src/internal/operators/timeout.ts b/src/internal/operators/timeout.ts index e62f04e3f5..1b7ccdd685 100644 --- a/src/internal/operators/timeout.ts +++ b/src/internal/operators/timeout.ts @@ -1,11 +1,6 @@ import { async } from '../scheduler/async'; -<<<<<<< HEAD -import { TimeoutError } from '../util/TimeoutError'; -import { MonoTypeOperatorFunction, SchedulerAction, SchedulerLike, TeardownLogic } from '../types'; -======= import { createTimeoutError } from '../util/TimeoutError'; import { MonoTypeOperatorFunction, SchedulerLike } from '../types'; ->>>>>>> feat(TimeoutError): is deprecated import { timeoutWith } from './timeoutWith'; import { throwError } from '../observable/throwError'; diff --git a/src/internal/util/UnsubscriptionError.ts b/src/internal/util/UnsubscriptionError.ts index 96e4353a9d..45433a195e 100644 --- a/src/internal/util/UnsubscriptionError.ts +++ b/src/internal/util/UnsubscriptionError.ts @@ -33,6 +33,7 @@ UnsubscriptionErrorImpl.prototype = Object.create(Error.prototype); export const UnsubscriptionError: UnsubscriptionErrorCtor = UnsubscriptionErrorImpl as any; export function createTeardownError(errors: any[]) { + // TODO(benlesh): Move to just using createRxError here. const error: any = new UnsubscriptionError(errors); error.__rxjsErrorCode = RxErrorCode.Teardown; return error;