Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to customise firebase DocumentData interface?

I'm using [email protected] and [email protected]

whenever I create a document from firestore i get an object of type firebase.firestore.DocumentReference as expected

If I call the get(options?: firebase.firestore.GetOptions|undefined) I'll get a object of type firebase.firestore.DocumentSnapshot as expected

If i call data(options?: firebase.firestore.DataOptions|undefined) I'll get either a firebase.firestore.DocumentData object or undefined, as expected

Now, on my manager object, I know what I'm writing to the databse, so I can make the assertion that whatever DocumentData you get out of my manager, you'll get a Client as shown

export interface Client {
    name: string;
    website?: string;
    description?: string;
    visible: boolean;
}

I want to create an interface for my manager object that expresses that. So I've tried:

export interface FirebaseDocumentSnapshot<T> extends $firebase.firestore.DocumentSnapshot {
    data(options?: $firebase.firestore.SnapshotOptions | undefined): T|undefined
}

export interface FirebaseDocumentReference<T> extends $firebase.firestore.DocumentReference {
    get(options?: $firebase.firestore.GetOptions | undefined): Promise<FirebaseDocumentSnapshot<T>>
}

The problem i've got is here:

const client: Client = mapClient(args);

const result: FirebaseDocumentReference<Client> = await $db.add(client);

the error is:

[ts]

Type 'DocumentReference' is not assignable to type 'FirebaseDocumentReference<Client>'.
  Types of property 'get' are incompatible.
    Type '(options?: GetOptions | undefined) => Promise<DocumentSnapshot>' is not assignable to type '(options?: GetOptions | undefined) => Promise<FirebaseDocumentSnapshot<Client>>'.
      Type 'Promise<DocumentSnapshot>' is not assignable to type 'Promise<FirebaseDocumentSnapshot<Client>>'.
        Type 'DocumentSnapshot' is not assignable to type 'FirebaseDocumentSnapshot<Client>'.
          Types of property 'data' are incompatible.
            Type '(options?: SnapshotOptions | undefined) => DocumentData | undefined' is not assignable to type '(options?: SnapshotOptions | undefined) => Client | undefined'.
              Type 'DocumentData | undefined' is not assignable to type 'Client | undefined'.
                Type 'DocumentData' is not assignable to type 'Client'.
                  Property 'name' is missing in type 'DocumentData'. [2322]
const result: FirebaseDocumentReference<Client>

How can i declare the interfaces so I can know the type of the resulting object?

like image 418
Simon Avatar asked Dec 07 '18 17:12

Simon


People also ask

How do I add Subcollection to Firebase cloud firestore?

The solution goes: Get the interested document (doc) and then: doc. ref. collection('Collection_Name'). add('Object_to_be_added') It is advisable to include a then/catch after the promise.

Is there any way to update a specific index from the array in firestore?

Is there any way to update a specific index from the array in Firestore? No, there is not! This is not possible because if you want to perform an update, you need to know the index of that particular element. When talking about Cloud Firestore arrays, the things are different that you might think.

What is DocumentReference in firebase?

A DocumentReference refers to a document location in a Cloud Firestore database and can be used to write, read, or listen to the location. There may or may not exist a document at the referenced location. A DocumentReference can also be used to create a CollectionReference to a subcollection.

How do I change the name of a document in firestore?

Another alternative is to enter the document path directly in the Path field, e.g. myCollection/myDocumentId and click the Run button. Right-click on the left-most field (ID column) of the document and choose Rename Document.


2 Answers

A simple solution would be to cast the return data:

const client = snapshot.data() as Client

like image 150
Gal Bracha Avatar answered Sep 25 '22 02:09

Gal Bracha


@gal-bracha's solution will "work" in the sense that the TypeScript compiler will assume client is of type Client, but it doesn't prevent a runtime error if bad data ended up in Firestore. A safer solution when dealing with any data external to your app is to use something like Yup to validate data explicitly:

import * as yup from "yup";

const clientSchema = yup.object({
  name: yup.string(),
  website: yup.string().url().notRequired(),
  description: yup.string().notRequired(),
  visible: yup.boolean()
});
// You can use this elsewhere in your app
export type Client = yup.InferType<typeof clientSchema>;

// Usage
const client = await clientSchema.validate(snapshot.data());

This is more defensive as clientSchema.validate will throw an error if, for some reason, bad data ended up in Firestore. After validate(), you're guaranteed, that client is a Client and not just telling the TypeScript compiler "treat this as a Client even though at runtime, it may not be".

like image 36
Mike Sukmanowsky Avatar answered Sep 24 '22 02:09

Mike Sukmanowsky