Typescript
The library comes with support for a full typesafe API.
Response types
When fetching a or modifying documents the default return type from
Firestore is a QuerySnapshot
or
DocumentSnapshot
whose data type is
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:
Explcit types
Provide the type declaration to the hooks directly:
type Product = {
name: string;
price: number;
}
useFirestoreQuery<Product>(...); // QuerySnapshot<Product>
useFirestoreQueryData<Product>(...); // Product[]
useFirestoreDocument<Product>(); // DocumentSnapshot<Product>
useFirestoreDocumentData<Product>(); // Product | null
Inferred types
The hooks will inferr any types from the provided reference, for example you could define Firestore converters:
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
Data modifications
When returning modified data, you can pass a second type to the hooks for typesafe result data.
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
}
Mutations
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:
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:
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:
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);
}