Skip to content

Commit

Permalink
Improve the ObservableLike type (#279)
Browse files Browse the repository at this point in the history
Co-authored-by: Ben Lesh <ben@benlesh.com>
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
3 people committed Oct 11, 2021
1 parent fb38d53 commit 7645829
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 7 deletions.
35 changes: 34 additions & 1 deletion source/observable-like.d.ts
Expand Up @@ -4,12 +4,45 @@ declare global {
}
}

/**
@remarks
The TC39 observable proposal defines a `closed` property, but some implementations (such as xstream) do not as of 10/08/2021.
As well, some guideance on making an `Observable` do not include `closed` propery.
@see https://github.com/tc39/proposal-observable/blob/master/src/Observable.js#L129-L130
@see https://github.com/staltz/xstream/blob/6c22580c1d84d69773ee4b0905df44ad464955b3/src/index.ts#L79-L85
@see https://github.com/benlesh/symbol-observable#making-an-object-observable
*/
export type Unsubscribable = {
unsubscribe(): void;
};

type OnNext<ValueType> = (value: ValueType) => void;
type OnError = (error: unknown) => void;
type OnComplete = () => void;

export type Observer<ValueType> = {
next: OnNext<ValueType>;
error: OnError;
complete: OnComplete;
};

/**
Matches a value that is like an [Observable](https://github.com/tc39/proposal-observable).
@remarks
The TC39 Observable proposal defines 2 forms of `subscribe()`:
1. Three callback arguments: `subscribe(observer: OnNext<ValueType>, onError?: OnError, onComplete?: OnComplete): Unsubscribable;`
2. A single `observer` argument: (as defined below)
But `Observable` implementations have evolved to preferring case 2 and some implementations choose not to implement case 1. Therefore, an `ObservableLike` cannot be trusted to implement the first case. (xstream and hand built observerables often do not implement case 1)
@see https://github.com/tc39/proposal-observable#observable
@see https://github.com/tc39/proposal-observable/blob/master/src/Observable.js#L246-L259
@see https://benlesh.com/posts/learning-observable-by-building-observable/
@category Basic
*/
export interface ObservableLike<ValueType = unknown> {
subscribe(observer: (value: ValueType) => void): void;
subscribe(observer?: Partial<Observer<ValueType>>): Unsubscribable;
[Symbol.observable](): ObservableLike<ValueType>;
}
22 changes: 16 additions & 6 deletions test-d/index.ts
Expand Up @@ -6,14 +6,24 @@ expectAssignable<symbol>(Symbol.observable);

const observable = (null as any) as ObservableLike;

observable.subscribe(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
observable.subscribe(value => {
expectType<unknown>(value);
const subscription = observable.subscribe({
next: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function
});
expectType<{unsubscribe(): void}>(subscription);

observable.subscribe({
next: value => {
expectType<unknown>(value);
},
});

const observable2 = (null as any) as ObservableLike<string>;

observable2.subscribe(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
observable2.subscribe(value => {
expectType<string>(value);
observable2.subscribe({
next: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function
});
observable2.subscribe({
next: value => {
expectType<string>(value);
},
});

0 comments on commit 7645829

Please sign in to comment.