Skip to content

Instantly share code, notes, and snippets.

@JamieCurnow
Last active April 26, 2024 21:20
Show Gist options
  • Star 57 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save JamieCurnow/cba3968a7f1e335d473632f9fc9f6e8b to your computer and use it in GitHub Desktop.
Save JamieCurnow/cba3968a7f1e335d473632f9fc9f6e8b to your computer and use it in GitHub Desktop.
Using Firestore with Typescript
/**
* This Gist is part of a medium article - read here:
* https://jamiecurnow.medium.com/using-firestore-with-typescript-65bd2a602945
*/
// import firstore (obviously)
import { firestore } from "firebase-admin"
// Import or define your types
// import { YourType } from '~/@types'
interface YourType {
firstName: string
lastName: string
isGreat: boolean
blackLivesMatter: true
}
interface YourOtherType {
something: boolean
somethingElse: boolean
}
// This helper function pipes your types through a firestore converter
const converter = <T>() => ({
toFirestore: (data: Partial<T>) => data,
fromFirestore: (snap: FirebaseFirestore.QueryDocumentSnapshot) => snap.data() as T
})
// This helper function exposes a 'typed' version of firestore().collection(collectionPath)
// Pass it a collectionPath string as the path to the collection in firestore
// Pass it a type argument representing the 'type' (schema) of the docs in the collection
const dataPoint = <T>(collectionPath: string) => firestore().collection(collectionPath).withConverter(converter<T>())
// Construct a database helper object
const db = {
// list your collections here
users: dataPoint<YourType>('users'),
userPosts: (userId: string) => dataPoint<YourOtherType>(`users/${userId}/posts`)
}
// export your helper
export { db }
export default db
/**
* Some examples of how to use:
*/
const example = async (id: string) => {
// firestore just as you know it, but with types
const userDoc = await db.users.doc(id).get()
const { blackLivesMatter } = userDoc.data()
return blackLivesMatter === true // obviously
}
const createExample = async (userId: string) => {
await db.userPosts(userId).doc().create({
something: false,
somethingElse: true
})
}
// Always use set for updates as firestore doesn't type update function correctly yet!
const updateExample = async (id: string) => {
await db.users.doc(id).set({
firstName: 'Jamie',
blackLivesMatter: true
}, { merge: true })
}
@fabiomoggi
Copy link

I can't destruct the typed object returning from doc.data().

export interface User {
  id: number;
  name: string;
  email: string;
}

// omitting converter & dataPoint functions

const db = {
  users: dataPoint<User>("users"),
};

Then in a Cloud Function I have:

const userDoc = await db.users.doc("12345").get();
const { email } = userDoc.data(); // error message: "Property 'email' does not exist on type 'Partial<User> | undefined'"

This should pretty much do the trick of having typed results from Firestore, however, the error message above keeps preventing typescript from compiling the code.

Any thoughts on this?

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment