Using Typescript with Firebase-admin results in rejection of all FieldValues

I am currently utilizing firebase-admin to manage a firestore database using Cloud Functions in TypeScript. I have encountered an issue where setting the type of my collection reference prevents me from using add with FieldValues. The add method now requires a literal object that matches the collection document structure, without any FieldValues. Can someone provide guidance on a recommended approach for using FieldValues with firebase-admin in TypeScript? Any solution utilizing the core libraries with FieldValues will suffice.

Below is a basic example involving FieldValue.serverTimestamp:

import { firestore, initializeApp } from 'firebase-admin'
import { firebaseConfig } from './secret'
import { https } from 'firebase-functions'

interface User {
  id?: string
  displayName: string
  createdAt: firestore.Timestamp
}
const converter = {
  toFirestore: (data: User) => data,
  fromFirestore: (snapshot: firestore.QueryDocumentSnapshot<User>): User => {
    const data = snapshot.data()
    const doc = { id: snapshot.id, ...data }
    return doc
  }
}
initializeApp(firebaseConfig)
const db = firestore()
const usersCollectionRef = db.collection('users').withConverter(converter)
export const cloudFunction = https.onCall(async (props, context) => {
  const newUserData = {
    displayName: 'New User',
    createdAt: firestore.FieldValue.serverTimestamp()
  }
  await usersCollectionRef.add(newUserData)
  // Issue arising due to incompatible argument types when using FieldValue with Timestamp
})

Similarly, with FieldValue.arrayUnion:

interface User {
  id?: string
  friendNames: string[]
}
const converter = {
  toFirestore: (data: User) => data,
  fromFirestore: (snapshot: firestore.QueryDocumentSnapshot<User>): User => {
    const data = snapshot.data()
    const doc = { id: snapshot.id, ...data }
    return doc
  }
}
initializeApp(firebaseConfig)
const db = firestore()
const usersCollectionRef = db.collection('users').withConverter(converter)
export const cloudFunction = https.onCall(async (props, context) => {
  await usersCollectionRef.add({
    friendNames: firestore.FieldValue.arrayUnion('BFF')
    // Conundrum caused by missing properties of 'string[]' when using FieldValue with arrayUnion

  })
})

Answer №1

Give this a try

import * as admin from 'firebase-admin'
import { Timestamp } from 'firebase-admin/firestore'

interface UserType {
  id?: string
  displayName: string
  createdAt: Timestamp
  // modification
  friends: string[]
}

....

// sets up the firestore handler
export const firestore = Object.assign(
  () => {
    return admin.firestore()
  }, {
    doc: <T>( path: string ) => {
      return admin.firestore()
        .doc( path )
        .withConverter<T>( converter<T>() )
    },
    collection: <T>( path: string ) => {
      return admin.firestore()
        .collection( path )
        .withConverter<T>( converter<T>() )
    },
    // modification
    fieldValue: admin.firestore.FieldValue
  } )
// custom converter for types
const converter = <T>() => ( {
  toFirestore: ( data: any ) => data,
  fromFirestore: ( doc: any ) => {
    return {
       ...doc.data(),
       id: doc.id
    } as T
  }
} )

then implement it in the following way:

import {firestore} from 'some/place'

...

// now you can define types when setting and retrieving data
const usersRef = firestore.collection<UserType>('/users')
await userRef.add({
  displayName: 'New User',
  // modification
  createdAt: firestore.fieldValue.serverTimestamp(),
  friends: firestore.fieldValue.arrayUnion('FRIEND_NAME')
})

Answer №2

If I'm not mistaken, it looks like specifying createdAt as type Timestamp should do the trick

import type { Timestamp } from 'firebase-admin/firestore';

interface User {
  displayName: string
  createdAt: Timestamp
}

This solution should resolve the issue at hand

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Get the base 64 file and convert it to an Excel spreadsheet

The encoded data in base 64 format is provided below. UEsDBBQAAAAIAIlimE8HQU1igQAAALEAAAAQAAAAZG9jUHJvcHMvYXBwLnhtbE2OPQsCMRBE/8px\nvbdBwUJiQNBSsLIPexsvkGRDskJ+vjnBj24ebxhG3wpnKuKpDi2GVI/jIpIPABUXirZOXaduHJdo\npWN5ADvnkc6Mz0hJYKvUHqgJpZnmTf4Ojka ...

Is there a workaround for utilizing reducer dispatch outside of a React component without relying on the store?

Recently, I implemented a reducer in my project that involves using react, typescript and nextJS. I am wondering if there is a method to trigger the reducer outside of a react component, such as from an API service. While searching for solutions, most re ...

Automatic browser refresh with the `bun dev` command

Currently experimenting with the latest bun platform (v0.1.6) in conjunction with Hono. Here are the steps I followed: bun create hono test-api cd test-api bun dev After running the server, the following message appears: $ bun dev [1.00ms] bun!! v0.1.6 ...

Using ES6 and Typescript, when a button is clicked, apply a class to all TD elements within the button except for the first one. No need for jQuery

A sample table structure is shown below: <table> <tr> <td>1</td> <td>joe</td> <td>brown</td> <td><button onclick="addClasses()">Add Class to add TD's in t ...

Angular's `await` feature does not wait for the function to complete its execution

Trying to call an async function from a Cordova plugin, but the await keyword doesn't seem to be working: export class MationLiteService implements IgatewayService { async getGroupAllInfo(gatewayId: string, account: string, decryptedpasswd: string) ...

Improving the structure of a TypeScript switch statement

Looking for a more efficient way to implement a switch statement in TypeScript? Here is the current code snippet from a component: switch (actionType) { case Type.Cancel { this.cancel(); break; } case Type.Discard { thi ...

Is excluding dependencies necessary for a modular Typescript project?

I am currently in the process of developing an NPM package for a Typescript project using gulp and browserify. The challenge I'm facing is that the consumer of the package does not utilize modules. Thus, I am working on creating a standalone bundle wi ...

What is the method by which the Material-UI Button component determines the properties for the component that is passed to the `component` prop

Could someone please clarify how Material-UI enhances the properties of its Button component by incorporating the properties of a specific component if passed in the component attribute? interface MyLinkProps extends ButtonBaseProps { someRandomProp: str ...

The attribute 'Error' is not available for the object of type 'MovieData | ResponseError'. This attribute is also not present in objects of type 'MovieData'

Question Answered I am currently working with two interfaces, MovieData and ResponseError. export interface MovieData { Poster: string; Title: string; Plot: string; imdbID: string; } The ResponseError interface looks like this: export interface R ...

Issue with the onClick event in next.js and TypeScript

While working on the frontend development of an app, I encountered a strange issue with the onClick function. The error message I'm seeing is: Type '(e: SyntheticEvent<Element, Event>) => void' is not assignable to type 'Custom ...

retrieve asynchronous data from the server using ngrx

How can I retrieve asynchronous data from the server? I am looking to save this data in a global store for future updates. I'm having trouble grasping the concept of asynchronous calls, such as in Redux. While I was able to understand it with simpl ...

Is it possible to utilize a partial entity for saving with TypeORM?

My current table structure looks like this: --changeset 0004-order:ccushing create table if not exists "order"."order" ( id uuid primary key not null default uuid_generate_v4(), state uuid re ...

What is the best way to extract items from another array that have approved IDs - is it through using map(), filter(),

I am unsure about the best approach to retrieve only the items (array with objects) that have been approved based on their id. Should I start by using map() and then filter(), or should I filter() them first and then map()? export class AppComponent { ...

"Unresolved Class / Interface Error: The variable 's' is not defined

Exploring Typescript and Angular2 for the first time has been a learning experience, especially when it comes to classes and interfaces. Take a look at my interface: export interface Discount { codSco: string; desSco: string; } Now I'm atte ...

What are the techniques for narrowing down this specific type in TypeScript?

Is there a way to modify the following code snippet to eliminate the need for as casting in order to pass the type check successfully? type SupportedHandlerType = string | number | Date type Handler<T> = (data: T[]) => void function example<T ...

When utilizing the Map.get() method in typescript, it may return undefined, which I am effectively managing in my code

I'm attempting to create a mapping of repeated letters using a hashmap and then find the first non-repeated character in a string. Below is the function I've developed for this task: export const firstNonRepeatedFinder = (aString: string): strin ...

Creating interfaces in Typescript without specifying keys allows for more flexibility in defining the structure of objects within the

Recently, I came across a particular dataset that looks like this: { Europe: { WestEurope: { Belgium: [French, English, Dutch] } } } I'm grappling with the challenge of creating an interface for such a dynamic structure, which essen ...

How to Maintain Default Styling in Next.js with Material UI When Disabling Accordion Feature

I am currently working on a project using Next.js and incorporating Material UI for the user interface elements. One particular challenge I am facing is with an Accordion component that needs to be disabled under specific conditions, but still appear witho ...

Setting a personalized username in Firebase: A step-by-step guide

Upon the initial login using the Google Auth provider, a "username" field with an empty value is assigned to the Users collection within the user's document. The system then checks if the username length surpasses 3 characters (which is the minimum re ...

What is the reason for the retrieval of jquery-3.5.1.min.js through the request.params.id expression?

For my school project, I am using Express.js with TypeScript to create a simple app. This router is used for the edit page of a contact list we are developing. It displays the ID of the current contact being edited in the search bar. The problem arises whe ...