Search code examples
rxjsangularfire2rxjs6

AngularFire2 promises into Observable type Issues


I'm trying to map angularfire2's promises into typed observables. This is my class:

export class RestService<T extends Entity> {

    constructor(
        protected afAuth: AngularFireAuth,
        protected store: AngularFirestore,
        protected readonly collectionName: string) {

        this.collection = store.collection<T>(collectionName);

    }

    protected collection: AngularFirestoreCollection<any>;

    all(): Observable<Array<T>> {
        return from(this.collection
            .snapshotChanges()
            .pipe(
                map(action => mapActionArray(action)),
                catchError((error: any) => {
                    throw new ApiError(error);
                })
            )
        );
    }


}

/**
 * Override Firestore errors with common app errors.
 * @param error - Firestore error
 */
function mapError(error: any): Observable<ApiError> {
    return throwError(new ApiError(error));
}

function mapDocument<T extends Entity>(doc: DocumentSnapshot<any>): T {
    return {
        id: doc.id,
        ...doc.data()
    };
}

Notice the all() method, I want to map any error in the catchError, but the thing is I get this type error:

TS2322: Type 'Observable<{}[]>' is not assignable to type 'Observable'.   Type '{}[]' is not assignable to type 'T[]'.     Type '{}' is not assignable to type 'T'.

I fixed it by changing the return type to Observable<{} | Array>, but that would require using the same types whenever I use this service, I don't find it good. How do I work around this? I want to be able to type my services and in beforehand map the errors into a proper CustomError object (this includes mapping codes to messages).

Btw, I also tried using the throwError from RXJS.

Thanks in advance.


Solution

  • Answering my own question, I had to use the catchError before the map operator. So:

      all(): Observable<Array<T>> {
        return from(this.collection
            .snapshotChanges()
            .pipe(
                map(action => mapActionArray(action)),
                catchError((error: any) => {
                    throw new ApiError(error);
                })
            )
        );
    }
    

    Would become

      all(): Observable<Array<T>> {
        return from(this.collection
            .snapshotChanges()             
            .pipe(
                catchError((error: any) => {
                    throw new ApiError(error);
                }),
                map(action => mapActionArray(action))                
            )
        );
    }
    

    Hope it helps