Skip to content

Commit

Permalink
fix(Subject): stricter types on next
Browse files Browse the repository at this point in the history
BREAKING CHANGE: TS 2.8 and lower will have issues with calling `next()` on `Subject<void>`, `undefined` will need to be passed, or should upgrade to the latest version fo TypeScript.
  • Loading branch information
benlesh committed Apr 3, 2020
2 parents 396e804 + 65e1bd7 commit 3e66e66
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 13 deletions.
37 changes: 37 additions & 0 deletions docs_app/content/guide/subject.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,40 @@ subject.complete();
```

The AsyncSubject is similar to the [`last()`](/api/operators/last) operator, in that it waits for the `complete` notification in order to deliver a single value.


## Void subject

Sometimes the emitted value doesn't matter as much as the fact that a value was emitted.

For instance, the code below signals that one second has passed.

```ts
const subject = new Subject<string>();
setTimeout(() => subject.next('dummy'), 1000);
```

Passing a dummy value this way is clumsy and can confuse users.

By declaring a _void subject_, you signal that the value is irrelevant. Only the event itself matters.

```ts
const subject = new Subject<void>();
setTimeout(() => subject.next(), 1000);
```

A complete example with context is shown below:

```ts
import { Subject } from 'rxjs';

const subject = new Subject(); // Shorthand for Subject<void>

subject.subscribe({
next: () => console.log('One second has passed')
});

setTimeout(() => subject.next(), 1000);
```

<span class="informal">Before version 7, the default type of Subject values was `any`. `Subject<any>` disables type checking of the emitted values, whereas `Subject<void>` prevents accidental access to the emitted value. If you want the old behavior, then replace `Subject` with `Subject<any>`.</span>
32 changes: 28 additions & 4 deletions spec/Subject-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@ import { delay } from 'rxjs/operators';

/** @test {Subject} */
describe('Subject', () => {

it('should allow next with empty, undefined or any when created with no type', (done: MochaDone) => {
const subject = new Subject();
subject.subscribe(x => {
expect(x).to.be.a('undefined');
}, null, done);

const data: any = undefined;
subject.next();
subject.next(undefined);
subject.next(data);
subject.complete();
});

it('should allow empty next when created with void type', (done: MochaDone) => {
const subject = new Subject<void>();
subject.subscribe(x => {
expect(x).to.be.a('undefined');
}, null, done);

subject.next();
subject.complete();
});

it('should pump values right on through itself', (done: MochaDone) => {
const subject = new Subject<string>();
const expected = ['foo', 'bar'];
Expand Down Expand Up @@ -271,7 +295,7 @@ describe('Subject', () => {
});

it('should not allow values to be nexted after it is unsubscribed', (done: MochaDone) => {
const subject = new Subject();
const subject = new Subject<string>();
const expected = ['foo'];

subject.subscribe(function (x) {
Expand Down Expand Up @@ -397,7 +421,7 @@ describe('Subject', () => {

it('should be an Observer which can be given to Observable.subscribe', (done: MochaDone) => {
const source = of(1, 2, 3, 4, 5);
const subject = new Subject();
const subject = new Subject<number>();
const expected = [1, 2, 3, 4, 5];

subject.subscribe(
Expand All @@ -414,7 +438,7 @@ describe('Subject', () => {

it('should be usable as an Observer of a finite delayed Observable', (done: MochaDone) => {
const source = of(1, 2, 3).pipe(delay(50));
const subject = new Subject();
const subject = new Subject<number>();

const expected = [1, 2, 3];

Expand All @@ -431,7 +455,7 @@ describe('Subject', () => {
});

it('should throw ObjectUnsubscribedError when emit after unsubscribed', () => {
const subject = new Subject();
const subject = new Subject<string>();
subject.unsubscribe();

expect(() => {
Expand Down
4 changes: 2 additions & 2 deletions spec/observables/from-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ describe('from', () => {
expect(nextInvoked).to.equal(false);
});
it(`should accept a function`, (done) => {
const subject = new Subject();
const handler: any = (...args: any[]) => subject.next(...args);
const subject = new Subject<any>();
const handler: any = (arg: any) => subject.next(arg);
handler[observable] = () => subject;
let nextInvoked = false;

Expand Down
2 changes: 1 addition & 1 deletion spec/operators/bufferCount-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('bufferCount operator', () => {
});

it('should buffer properly (issue #2062)', () => {
const item$ = new Subject();
const item$ = new Subject<number>();
const results: any[] = [];
item$.pipe(
bufferCount(3, 1)
Expand Down
2 changes: 1 addition & 1 deletion spec/operators/skipUntil-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ describe('skipUntil', () => {
const e1 = hot( '--a--b--c--d--e--|');
const e1subs = ['^ !',
'^ !']; // for the explicit subscribe some lines below
const skip = new Subject();
const skip = new Subject<string>();
const expected = '-----------------|';

e1.subscribe((x: string) => {
Expand Down
4 changes: 2 additions & 2 deletions src/internal/Subject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class SubjectSubscriber<T> extends Subscriber<T> {
*
* @class Subject<T>
*/
export class Subject<T> extends Observable<T> implements SubscriptionLike {
export class Subject<T = void> extends Observable<T> implements SubscriptionLike {

[rxSubscriberSymbol]() {
return new SubjectSubscriber(this);
Expand Down Expand Up @@ -58,7 +58,7 @@ export class Subject<T> extends Observable<T> implements SubscriptionLike {
return <any>subject;
}

next(value?: T) {
next(value: T) {
if (this.closed) {
throw new ObjectUnsubscribedError();
}
Expand Down
2 changes: 1 addition & 1 deletion src/internal/operators/repeatWhen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class RepeatWhenOperator<T> implements Operator<T, T> {
*/
class RepeatWhenSubscriber<T, R> extends OuterSubscriber<T, R> {

private notifications: Subject<any> | null = null;
private notifications: Subject<void> | null = null;
private retries: Observable<any> | null = null;
private retriesSubscription: Subscription | null | undefined = null;
private sourceIsBeingSubscribedTo: boolean = true;
Expand Down
2 changes: 1 addition & 1 deletion src/internal/operators/share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Subject } from '../Subject';
import { MonoTypeOperatorFunction } from '../types';

function shareSubjectFactory() {
return new Subject();
return new Subject<any>();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/internal/operators/windowTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ interface CloseState<T> {
class CountedSubject<T> extends Subject<T> {
private _numberOfNextedValues: number = 0;

next(value?: T): void {
next(value: T): void {
this._numberOfNextedValues++;
super.next(value);
}
Expand Down

0 comments on commit 3e66e66

Please sign in to comment.