Search code examples
angulartypescriptfirebasegoogle-cloud-firestore

Unable to convert to type from Firestore in Angular


I'm trying to convert data from Firestore into a type in Angular. Though I'm struggling on how to use a converter.

This is my model:

import { QueryDocumentSnapshot } from "@angular/fire/firestore";

export interface Song {
  title: string;
  lyrics: string;
}

export const songConverter = {
  toFirestore: (data: Song) => data,
  fromFirestore: (snapshot: QueryDocumentSnapshot) => snapshot.data() as Song
}

And this is my service:

import { Injectable, inject } from '@angular/core';
import { Firestore, collection, doc, getDoc, getDocs } from '@angular/fire/firestore';

import { Song, songConverter } from './song.model';

@Injectable({
  providedIn: 'root'
})
export class SongService {
  firestore: Firestore = inject(Firestore);

  getSongs(): Promise<Song[]> {
    return getDocs(collection(this.firestore, 'songs').withConverter(songConverter));
  }

  getSong(id: string): Promise<Song> {
    return getDoc(doc(this.firestore, 'songs', id).withConverter(songConverter));
  }
}

The errors I receive are:

Type 'Promise<DocumentSnapshot<Song, Song>>' is not assignable to type 'Promise'. Type 'DocumentSnapshot<Song, Song>' is missing the following properties from type 'Song': title, lyrics

and:

Type 'Promise<QuerySnapshot<Song, Song>>' is not assignable to type 'Promise<Song[]>'. Type 'QuerySnapshot<Song, Song>' is missing the following properties from type 'Song[]': length, pop, push, concat, and 28 more.

Where am I going wrong?


Solution

  • The code you want looks something like this. I'd use map to extract the Song object from each DocumentSnapshot in the QuerySnapshot.

    function getSongs(): Promise<Song[]> {
      return getDocs(collection(this.firestore, 'songs').withConverter(songConverter))
        .then((querySnap: QuerySnapshot<Song, Song>) => {
          return querySnap.docs.map((docSnap: DocumentSnapshot<Song, Song>) => docSnap.data());
        })
    }
    

    Edit: or use async/await for improved readability.

    async function getSongs(): Promise<Song[]> {
      const querySnap: QuerySnapshot<Song, Song> = 
      await getDocs(collection(this.firestore, 'songs').withConverter(songConverter));
      return querySnap.docs.map((docSnap: DocumentSnapshot<Song,Song>) => docSnap.data());
    }