Симпатичный небольшой шаблон проектирования кода, который повысит производительность вашего Firestore и сделает ваш код безопасным с помощью TypeScript.
TL;DR:Просто посмотрите этот репозиторий, чтобы посмотреть пример кода:
Если вы ищете способ добавить TypeScript в SDK Firebase V8, ознакомьтесь с другими моими статьями:
Давайте код!
Итак, недавно я поиграл с новым SDK Firebase/Firestore V9 и придумал очень простой и минимальный способ заставить его хорошо работать с TypeScript.
Единственное, что мы собираемся сделать, это создать абстракцию, которая сопоставляет все возможные коллекции в нашей БД и возвращает типизированную ссылку на коллекцию Firestore. Так как я поклонник Vue и парадигмы компонуемого кодирования, я собираюсь поместить эту абстракцию в каталог с именем composables и в файл с именем useDb.ts. Первое, что мы сделаем в этом файле, — инициализируем приложение firebase и получим ссылку на базу данных Firestore:
// /composables/useDb.ts
// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()
Далее мы собираемся создать небольшую функцию, которая будет принимать имя коллекции и выдавать ссылку на коллекцию Firestore. Эта ссылка может быть передана в getDoc или getDocs и т. д. для чтения/записи данных:
// /composables/useDb.ts
// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore, collection } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()
// This is just a helper to add the type to the db responses
const createCollection = (collectionName: string) => {
return collection(firestore, collectionName)
}
Прямо сейчас эта функция на самом деле мало что делает и в некотором роде бессмысленна, ноа вот и магия... Мы собираемся сделать эту функцию универсальным типом и добавить правильный тип в коллекцию. ссылка, которую он возвращает:
// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore, CollectionReference, collection, DocumentData } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()
// This is just a helper to add the type to the db responses
const createCollection = <T = DocumentData>(collectionName: string) => {
return collection(firestore, collectionName) as CollectionReference<T>
}
Довольно просто. Довольно лаконично.
Теперь мы импортируем все наши типы из любого места, где они определены, и экспортируем набор констант, запускающих функцию createCollection, с правильным путем и типом коллекции:
// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore, CollectionReference, collection, DocumentData } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()
// This is just a helper to add the type to the db responses
const createCollection = <T = DocumentData>(collectionName: string) => {
return collection(firestore, collectionName) as CollectionReference<T>
}
// Import all your model types
import { User } from 'src/types/User'
import { Author } from 'src/types/Author'
import { Book } from 'src/types/Book'
// export all your collections
export const usersCol = createCollection<User>('users')
export const authorsCol = createCollection<Author>('authors')
export const booksCol = createCollection<Book>('books')
Теперь у вас есть только один список имен коллекций и их типов!
Итак, как мне это использовать?
Ну, это так просто, как кажется! Допустим, мы хотим добавить нового пользователя в базу данных, мы просто импортируем usersCol из useDb и используем его в качестве первого аргумента функции Firestore setDoc:
// /someOtherFile.ts
import { doc, setDoc } from '@firebase/firestore'
import { usersCol } from './composables/useDb'
export const setJamiesUser = async () => {
const userRef = doc(usersCol, 'user_12345')
await setDoc(userRef, {
age: 30,
firstName: 'Jamie',
lastName: 'Curnow'
})
}
Если вы попробуете это сделать, вы увидите, что мы получаем подсказки типов и ошибки компилятора, если мы даем setDoc неправильный тип данных для пользователя!
Другой пример: получение данных... Допустим, мы хотим получить все документы из коллекции books в базе данных и console.log() заголовков каждой книги:
// /anotherFile.ts
import { getDocs } from '@firebase/firestore'
import { booksCol } from './composables/useDb'
export const logBookTitles = async () => {
const bookDocs = await getDocs(booksCol)
bookDocs.docs.forEach((bookDoc) => {
const book = bookDoc.data()
console.log(book.title)
})
}
Здесь typescript знает, что bookDoc.data() возвращает объект типа Book, и позволяет нам записать название книги. 🥳
Использование этого способа также будет работать для глубоко вложенных ключей при обновлении документов, что совершенно потрясающе:
import { doc, updateDoc } from '@firebase/firestore'
import { booksCol } from './composables/useDb'
export const updateBook = async () => {
const bookDocRef = doc(booksCol, 'book_12345')
await updateDoc(bookDocRef, {
'meta.created': new Date().toISOString()
})
}
В этом примере набирается meta.created! Удивительно!

Подведение итогов
Это такая приятная функция нового SDK V9 Firestore, и команда Firestore проделала фантастическую работу по ее созданию 🙌.
Если вы хотите увидеть минимальный репозиторий, реализующий этот шаблон, оформите заказ и поставьте звездочку:
Не забудьте немного поаплодировать этой истории, если она показалась вам полезной — ваши аплодисменты заставляют меня публиковать контент и дают мне небольшой стимул писать больше каждый раз, когда я вижу, что что-то выходит!
Поддержите своих создателей!
Если вы нашли эту историю полезной и хотели бы, чтобы я продолжал писать полезный контент, пожалуйста, поддержите меня на Patreon 🤗
Дополнительные материалы на plainenglish.io