tqf
Version:
React Query hooks for Firebase.
141 lines (101 loc) • 3.39 kB
text/mdx
---
title: TypeScript | Firestore
description: TypeScript usage with React Query Firebase.
---
The library comes with support for a full typesafe API.
When fetching a or modifying documents the default return type from
Firestore is a [`QuerySnapshot`](https://firebase.google.com/docs/reference/js/firestore_.querysnapshot) or
[`DocumentSnapshot`](https://firebase.google.com/docs/reference/js/firestore_.documentsnapshot) whose data type is
[`DocumentData`](https://firebase.google.com/docs/reference/js/firestore_.documentdata).
For a type safe application, this is dangerous. There are 2 ways to ensure your data
is returned type safe, either explicilty or inferred:
Provide the type declaration to the hooks directly:
```ts
type Product = {
name: string;
price: number;
}
useFirestoreQuery<Product>(...); // QuerySnapshot<Product>
useFirestoreQueryData<Product>(...); // Product[]
useFirestoreDocument<Product>(); // DocumentSnapshot<Product>
useFirestoreDocumentData<Product>(); // Product | null
```
The hooks will inferr any types from the provided reference, for example you could
define [Firestore converters](https://firebase.google.com/docs/reference/js/firestore_.firestoredataconverter):
```ts
type Product = {
name: string;
price: number;
}
const ref = collection(firebase, 'products').withConverter<Product>(...);
useFirestoreQuery('...', ref); // QuerySnapshot<Product>
useFirestoreQueryData('...', ref); // Product[]
const docRef = ref.doc('123');
useFirestoreDocument('...', docRef); // DocumentSnapshot<Product>
useFirestoreDocumentData('...', docRef); // Product | null
```
When returning modified data, you can pass a second type to the hooks for
typesafe result data.
```ts
type Product = {
name: string;
price: number;
};
const query = useFirestoreQuery<Product, number | null>("...", ref, undefined, {
select(snapshot: QuerySnapshot<Product>): number | null {
if (!snapshot.exists()) {
return null;
}
return snapshot.data().price;
},
});
if (query.isSuccess) {
const price = query.data; // number | null
}
```
When working with the various mutation hooks, you can provide types to
override the expected mutation data.
By default, the mutation value will be inferred from the provided reference, for example:
```ts
type Product = {
name: string;
price: number;
}
const ref = collection(firebase, 'products').withConverter<Product>(...);
const mutation = useFirestoreCollectionMutation(ref);
// mutate expects a Product
mutation.mutate({
name: 'New product',
price: 10,
});
```
You can also optionally provide a type override:
```ts
const mutation = useFirestoreCollectionMutation<Product>(ref);
// mutate expects a Product
mutation.mutate({
name: "New product",
price: 10,
});
```
If working with transactions, you can provide a custom type as the expected
response of the transaction:
```ts
const ref = collection(firebase, "posts").doc("123");
const mutation = useFirestoreTransaction<number>(firestore, async (tsx) => {
const post = await tsx.get(ref);
const newLikes = post.data().likes + 1;
tsx.update(ref, { likes: newLikes });
// Returning a number is required
return newLikes;
});
if (mutation.isSuccess) {
console.log("New likes: ", mutation.data);
}
```