3

I am trying to implement Repository Pattern with Firestore Firebase and TypeScript.

Code:

import { firestore } from "firebase-admin";
import { ISearchCriteria } from './ISearchCriteria'

//export class selectQuery<T> {
//    constructor(private query: firestore.Query<T>) { }
//}

export class DBContext {
    static current: DBContext = new DBContext();
    private db: firestore.Firestore;

    private constructor() {
        this.db = firestore();
    }

    collection(collectionName: string): firestore.CollectionReference<firestore.DocumentData> {
        return this.db.collection(collectionName);
    }

    public async where<T extends object>(collectionName: string, searchCriteria: ISearchCriteria[]): Promise<T[]> {
        var snapShot = this.collection(collectionName);
        for (var i = 0; i < searchCriteria.length; i++) {
            snapShot = snapShot.where(searchCriteria[i].fieldName, searchCriteria[i].filterOp, searchCriteria[i].value);
        }
        let res = await snapShot.get();
        return res.docs.map<T>(doc => ({ id: doc.id, ...doc.data() }) as T);
    }


    async get<T extends object>(collectionName: string): Promise<T[]> {
        let res = await this.collection(collectionName).get();
        return res.docs.map<T>(doc => ({ id: doc.id, ...doc.data() } as T));
    }

    async create<T extends object>(collectionName: string, item: T): Promise<T> {
        return (await this.collection(collectionName).add(item)) as T;
    }

    async update<T extends object>(collectionName: string, id: string, item: T): Promise<firestore.WriteResult> {
        var docRef = this.collection(collectionName).doc(id);
        return await docRef.update(item);
    }

    async delete<T extends object>(collectionName: string, id: string): Promise<firestore.WriteResult> {
        return await this.collection(collectionName).doc(id).delete();
    }
}

I followed the example here: Firestore: Multiple conditional where clauses.

But i am getting the following error : dbcontext.ts:23:13 - error TS2740: Type 'Query<DocumentData>' is missing the following properties from type 'CollectionReference<DocumentData>': id, parent, path, listDocuments, and 2 more. its failing in the mentioned below line

            snapShot = snapShot.where(searchCriteria[i].fieldName, searchCriteria[i].filterOp, searchCriteria[i].value);

ISearchCriteria:

    import { firestore } from "firebase-admin";
    export interface ISearchCriteria {
        fieldName: string | firestore.FieldPath,
        filterOp: FirebaseFirestore.WhereFilterOp,
        value: any
    }
Hany Habib
  • 1,377
  • 1
  • 10
  • 19

2 Answers2

5

The problem is that your collection() method returns a CollectionReference, but where() returns a Query. TypeScript is telling you that you can't assign a Query object to a CollectionReference variable.

If you look at the API documentation, you'll see that CollectionReference is actually a subclass of Query (it just returns all documents in the collection). When you call where(), you're building a new Query that filters the documents from the prior Query.

Try making your collection() return a Query instead so you can safely reassign snapShot:

collection(collectionName: string): firestore.Query<firestore.DocumentData> {
    return this.db.collection(collectionName);
}

BTW, your variable called snapShot isn't a snapshot - it should probably be called query.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • makes sense.. but to support the add/delete functionality like this i need to cast it back to collectionreference ? – Hany Habib Mar 25 '20 at 22:14
  • I don't know what you mean. Add/delete what? Once the query is executed with get(), you don't need that object any more - you have the results. – Doug Stevenson Mar 25 '20 at 22:15
  • If you mean that you want to use the collection method for other operations, then just cast the CollectionReference to a Query if you want to use it to build a query. – Doug Stevenson Mar 25 '20 at 22:17
1
const getDocuments = (collection: string, queries: QueryWhereClause[]) => {
  let collectionRef: firebase.firestore.Query<firebase.firestore.DocumentData> = firestore.collection(collection);
  if (queries && queries.length) {
    for (const query of queries) {
      collectionRef = collectionRef.where(
        query.field,
        query.condition,
        query.value
      );
    }
  }
  return collectionRef.get();
};

Adding (firebase.firestore.Query) as my collection type worked.

let collectionRef: firebase.firestore.Query<firebase.firestore.DocumentData> = firestore.collection(collection);
Gaurav Saluja
  • 623
  • 6
  • 8