Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript interface conformance with firestore queries

How do I work with Firebase and my own Typescript interfaces?

ie. I have a function here that I'm going to need the full Payment object, (not just the delta), so I get that via the DocumentReference object.

function doSomethingWithPayment(payment: Payment) {
   console.log(payment.amount); 
}


exports.resolveStripeCharge = functions.firestore.document('/payments/{paymentId}')
    .onWrite((change : Change <DocumentSnapshot>) => {

        change.after.ref.get().then((doc: DocumentSnapshot )=> {
            const payment : Payment =   doc.data(); // tslint warning here. 
            doSomethingWithPayment(payment); 

        })


[ts]
Type 'DocumentData' is not assignable to type 'Payment'.
  Property 'amount' is missing in type 'DocumentData'.
const payment: Payment

I get that the returned object wont necessarily conform the interface - but in anycase -what should I do here - if I want to enforce typing?

Do I just use:

 const payment : Payment =   <Payment>doc.data();

Or is there a much nicer solution?

like image 676
dwjohnston Avatar asked Jul 31 '18 05:07

dwjohnston


3 Answers

Given that doc.data() always returns a DocumentData, which is just {[field: string]: any}, I don't think there's any better way to turn that into a Payment than casting. You could write:

const data = doc.data();
const payment = {amount: doc.amount, /*and so on for the remaining members*/};

without a cast, but I don't think that's better in practical terms. It would make more sense to me for DocumentData to be just any (like the return of JSON.parse to give one example); you could file an issue on the project to propose that.

like image 51
Matt McCutchen Avatar answered Oct 26 '22 17:10

Matt McCutchen


Not sure if this helps, but the way I think of it is that the type you have in your program and the types you send and recieve from Firestore are different types.

So, typescript has this 'Partial' feature that takes an interface and makes all of the fields optional. That way even if the object isn't complete, it will still conform and provide you with intellisense.

So, I do this : export type dataUpdate<T> = Partial<T>;

Then, I do : const userPayload: dataUpdate<User> = snapshot/typecase as User

like image 34
George43g Avatar answered Oct 26 '22 16:10

George43g


I used const payment = doc.data() as Payment; I don't know since which version of typescript this is supported.

like image 2
manu Avatar answered Oct 26 '22 17:10

manu