I was working on an Angular project, and noticed a warning when subscribing to an Observable. I had something like:

view plain print about
1myObservableHere.subscribe((result: any) => {
2 console.log(result);
3 }, (err: any) => {
4 console.log(err);
5 });

I subscribe to the Observable, then put a comma separated list of methods. The first one is the next() method, and next comes the error() method, and finally we get a complete() method--left out of the sample above.

I've been using this syntax for what feels like forever, but today I was noticing a deprecated warning:

Use an observer instead of an error callback

I am using an Observable, why is this causing issues? First, it says to use an Observer, not an Observable. There is a distinction between them. The Observer is the value inside the Observable that is used to trigger the next, completed, or error messages. For most of my development this detail is hidden from me.

Instead of listing out each individual functions, we now can pass in an object and name them.

The new syntax would be like this:

view plain print about
1myObservableHere.subscribe({
2 next: (result: any) =>
{
3 console.log(result);
4 },
5 error: (err: any) => {
6 console.log(err);
7 },
8 complete: () => {
9 console.log('complete');
10 }
11 });

The Observer that we pass in, is an object, and it contains three properties: next, error, and complete. Each one represents a function that will be triggered automatically from the observable.

If you drill into the code, you find some interesting things. In IntelliJ I just control-clicked on the subscribe() method to open up Observable.ts:

view plain print about
1subscribe(observer?: PartialObserver<T>): Subscription;
2 /** @deprecated Use an observer instead of a complete callback */
3 subscribe(next: null | undefined, error: null | undefined, complete: () =>
void): Subscription;
4 /** @deprecated Use an observer instead of an error callback */
5 subscribe(next: null | undefined, error: (error: any) => void, complete?: () => void): Subscription;
6 /** @deprecated Use an observer instead of a complete callback */
7 subscribe(next: (value: T) => void, error: null | undefined, complete: () => void): Subscription;
8 subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;

First, while three options are deprecated, the fourth one is not and our first syntax is okay. We only run into problems if the next, error, or complete function are null or undefined.

But, look at the first item int he list. The argument it expects is a PartialObserver; not an Observer. I control-clicked again to go to a types.ts file:

view plain print about
1export type PartialObserver<T> = NextObserver<T> | ErrorObserver<T> | CompletionObserver<T>;

A PartialObserver is a union type, and can either be a NextObserver, ErrorObserver, or CompletionObserver. Each class definition includes a required method, and the remaining are optional:

view plain print about
1export interface NextObserver<T> {
2 closed?: boolean;
3 next: (value: T) =>
void;
4 error?: (err: any) => void;
5 complete?: () => void;
6}
7
8export interface ErrorObserver<T> {
9 closed?: boolean;
10 next?: (value: T) => void;
11 error: (err: any) => void;
12 complete?: () => void;
13}
14
15export interface CompletionObserver<T> {
16 closed?: boolean;
17 next?: (value: T) => void;
18 error?: (err: any) => void;
19 complete: () => void;
20}

The deprecated message was a bit confusing to dissect and solve, but by drilling into the code I was able to figure it out.