Skip to content

Instantly share code, notes, and snippets.

@JamieCurnow
Last active September 30, 2025 14:14
Show Gist options
  • Select an option

  • Save JamieCurnow/cba3968a7f1e335d473632f9fc9f6e8b to your computer and use it in GitHub Desktop.

Select an option

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 })
}
@no13bus
Copy link

no13bus commented Aug 3, 2025

This uses Firebase v10.4.0, based on the withConverter example in the docs: https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects

Instead of getting the document, this just returns the reference. The document is converted to/from a model class instance.

/* eslint-disable @typescript-eslint/no-explicit-any */
import {
  getFirestore,
  doc,
  type FirestoreDataConverter,
  type PartialWithFieldValue,
  type DocumentData,
  type QueryDocumentSnapshot
} from 'firebase/firestore'
import firebaseApp from '../config'
import { User } from './models/User'

const db = getFirestore(firebaseApp)

const converter = <T>(ModelClass: new (data: any) => T): FirestoreDataConverter<T> => ({
  toFirestore: (data: PartialWithFieldValue<T>): PartialWithFieldValue<DocumentData> =>
    data as PartialWithFieldValue<DocumentData>,
  fromFirestore: (snapshot: QueryDocumentSnapshot<DocumentData>): T => {
    const data = snapshot.data()
    return new ModelClass(data) as T
  }
})

const typedRef = <T>(ModelClass: new (data: any) => T, path: string, ...pathSegments: string[]) => {
  return doc(db, path, ...pathSegments).withConverter(converter<T>(ModelClass))
}

const docRef = {
  user: (uid: string) => typedRef<User>(User, 'users', uid)
}

export { docRef }

// Example

const ref = docRef.user(uid)
const docSnap = await getDoc(ref)
if (docSnap.exists()) {
  // Convert to User
  const user = docSnap.data()
  // Use User instance method
  console.log(user.toString())
} else {
  console.log('No such document!')
}

@drichar hi, this is very nice function. Actually I have a question about this, if the data class model have a Date type, when get the data from forestore, it should transfer Timestamp type to typescript Date type. Another question is that, how to support FieldValue type for data class model. For example:

class User {
name: string;
age: number;
createAt: Date
} 

const user = new User("daniel", 23, new Date())
// acutually I want to support **new User("daniel", FielValue.increment(1), FielValue.serverTimestamp())**,
 it means any field in the class maybe can be FieldValue type
docRef.set(user)

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