Troubleshooting in Firebase and Typescript: Trouble with ID property and withConverter when creating a new Document without an ID property

Allow me to elaborate on the current scenario:

  1. I am utilizing the add method from collection in Firestore to create a new document
  2. For maintaining type safety with TypeScript, I employ the withConverter
  3. When creating a new document, my object does not initially have an ID property
  4. Once the object is created, the ID property is then available

By the way, Mobx-state-tree is being utilized

The code

Let's go through it step by step

The Company model defined using mobx-state-tree

const CompanyModel = types.model({
  id: types.identifier,
  name: types.string
});

The required Types

Following the call of the add method, the CompanyReference is what I receive from Firestore

type CompanyReference = firebase.firestore.DocumentReference<
  Instance<typeof CompanyModel>
>;

TCompanySnapshot represents what is sent to Firestore when using the add method

type TCompanySnapshot = Omit<SnapshotIn<typeof CompanyModel>, "id">;

Converting to and from Firestore using withCOnverter

  • Send data to Firestore using the regular TCompanySnapshot object
  • Receive some object from Firestore (now with the ID property) and convert it into CompanyModel
const createCompanyConverter: firebase.firestore.FirestoreDataConverter<Instance<
  typeof CompanyModel
>> = {
  toFirestore(modelObject: TCompanySnapshot): firebase.firestore.DocumentData {
    return modelObject;
  },
  fromFirestore(
    snapshot: firebase.firestore.QueryDocumentSnapshot<TCompanySnapshot>,
    options: firebase.firestore.SnapshotOptions
  ): Instance<typeof CompanyModel> {
    const data = snapshot.data(options);
    return CompanyModel.create({
      id: snapshot.id,
      ...data
    });
  }
};

The issue

Now, when attempting to create a method for adding a new company, a typing error occurs

function addCompany(
  // companyData: SnapshotIn<typeof CompanyModel>,
  companyData: TCompanySnapshot
): Promise<CompanyReference> {
  const added = firestore
    .collection("companies")
    // .withConverter<Instance<typeof CompanyModel>>(createCompanyConverter)
    .withConverter<TCompanySnapshot>(createCompanyConverter)
    .add(companyData);

  return added;   // <<<<<------ ERROR HERE
}

The 'id' property is missing in the type

You can view this error on CodeSandbox

https://codesandbox.io/s/firebase-typescript-mobx-state-tree-n9s7o?file=/src/index.ts

Answer №1

The problem you're experiencing stems from your usage of withConverter(), which alters the type of the CollectionReference causing it to not align with your CompanyReference.

Refer to Firestore#collection(),

CollectionReference#withConverter()
, and CollectionReference#add():

firestore
  .collection("companies")                                 // CollectionReference<DocumentData>
  .withConverter<TCompanySnapshot>(createCompanyConverter) // CollectionReference<TCompanySnapshot>  
  .add(companyData);                                       // Promise<DocumentReference<TCompanySnapshot>>

Keep in mind that

DocumentReference<TCompanySnapshot> !== DocumentReference<Instance<typeof CompanyModel>>
.

If you are satisfied with how your object is being converted, you can forcefully change the type by using this line:

return added as Promise<CompanyReference>;

Nevertheless, it's advisable to rectify your FirestoreDataConverter setup. There's no requirement to excessively specify types since defining

firebase.firestore.FirestoreDataConverter<Instance<typeof CompanyModel>>
should propagate the correct types to other objects.

const createCompanyConverter: firebase.firestore.FirestoreDataConverter<Instance<typeof CompanyModel>> = {
  toFirestore(modelObject) {
    const objToUpload = { ...modelObject } as firebase.firestore.DocumentData; // DocumentData is mutable
    delete objToUpload.id; // ensure ID isn't uploaded to the document
    return objToUpload;
  },
  fromFirestore(snapshot, options) {
    const data = snapshot.data(options); // "as Omit<Instance<typeof CompanyModel>, "id">" could be added here

    // prioritize spreading data first to prevent incorrect id storage
    return CompanyModel.create({
      ...data, 
      id: snapshot.id
    });
  }
};

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

Potential uncertainty in Angular FormControl email validation due to potential null Object

Whenever I run the command ng s --aot, I encounter this message: Object is possibly 'null'. I've been trying various solutions all morning to resolve it, but without success. The issue seems to be related to objects like email.valid, dirty, ...

Modifying the Iterator Signature

My goal is to simplify handling two-dimensional arrays by creating a wrapper on the Array object. Although the code works, I encountered an issue with TypeScript complaining about the iterator signature not matching what Arrays should have. The desired fu ...

Navigating to the tsconfig.json file based on the location of the file being linted

In my monorepo, each package currently contains a .eslintrc.cjs file with the following setup: Package-specific ESLint Configuration const path = require('path') const ts = require('typescript') const OFF = 0 const WARN = 1 const ERROR ...

Tips for creating a console.log wrapper specifically designed for Angular2 using Typescript

Is there a way to create a custom global logging function in Angular 2 TypeScript project that can be used instead of console.log for services and components? I envision the function looking like this: mylogger.ts function mylogger(msg){ console.log ...

What is it about Kyle Simpson's OLOO methodology that seems to swim against the tide of Typescript's popularity?

Disclaimer: this post might come across as impulsive. Warning for Typescript beginners! Also, a bit of a vent session. Recently, I delved into the OLOO approach from the YDKJS book series within a Typescript and Node environment. // ideal JS syntax le ...

Error: Issue with accessing the 'get' property of an undefined value (Resolved issue with incompatible imports not functioning)

Encountering an issue while attempting to execute the karma TS spec file. Despite all modules and imports functioning properly without conflicts, the error persists. I've tried incorporating component.ngOninit() into beforeEach() and it(), but to no a ...

Retrieving data from an Array

I've encountered a peculiar issue while working on a test project. It seems that I am unable to access values in an array. pokemonStats$: Observable<PokemonStats[]>; getPokemonStats(id: number): any { this.pokemonStats$ .pipe(take(1)) .subscrib ...

Incorporate an SCSS file into the Stackblitz project

Is there a way to include an scss file in the stackblitz? I attempted it, but encountered some issues. Could you take a look and advise me on what to do? I also made an attempt to add home.html This is the project: Check out the stackblitz project here! ...

The JSX element with type 'Component' does not support any construct or call signatures at this time

I am facing an issue with the following code snippet const pageArea = (Component:ReactNode,title:string) => ({ ...props }) => { return ( <> <div> { Component && (<Component { ...

At what point do we employ providers within Angular 2?

In the Angular 2 documentation, they provide examples that also use HTTP for communication. import { HTTP_PROVIDERS } from '@angular/http'; import { HeroService } from './hero.service'; @Component({ selector: 'my-toh&ap ...

Issue [ERR_PACKAGE_PATH_NOT_EXPORTED]: Missing "exports" declaration - Firebase

Currently following a Full Stack React & Firebase Tutorial and attempting to implement Firebase Authentication for creating a new user. However, I encountered the following error: Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defin ...

The error property is not found in the type AxiosResponse<any, any> or { error: AxiosError<unknown, any>; }

As a beginner with typescript, I am encountering some issues with the following code snippet import axios, { AxiosResponse, AxiosError } from 'axios'; const get = async () => { const url = 'https://example.com'; const reques ...

What in the world is going on with this Typescript Mapped type without a right-hand side?

I encountered a situation where my React component had numerous methods for toggling boolean state properties. Since these functions all did the same thing, I wanted to streamline the process by creating a common function for toggling properties. Each met ...

Loop through a Map containing a List of objects in Angular 4

export class Individual { name: string, age: number, addressList: Map<string, Addresses[]>; } I am looking to extract and analyze all addresses of each individual. <div *ngFor="let person of individuals"> <div *ngFor="let addr o ...

Angular efficient approach to changing object properties

When working on creating or updating records, I encounter a problem with the length and cleanliness of my code. The dealTypeValues object varies based on the dealDispositionType (buyout or sale), resulting in lengthy and messy code. Does anyone have sugge ...

Angular's AsyncValidatorFn is triggered by the onblur event and does not work with keypress events

I'm currently working with the latest version of Angular and I'm attempting to implement a custom validation for checking a code through a RestAPI. The example below is functional, but it doesn't trigger on keypress events; it only activates ...

Angular 2 - Utilizing a Shared Service for Subscriptions

After referencing this guide: Parent and children communicate via a service I have decided to utilize a shared service instead of EventEmitter. The reason being that EventEmitter only facilitates communication between parent and child components, which do ...

Using TypeScript to access global variables from inside a method

Hi everyone, I'm trying to figure out how to access variables from the global scope (this) within 2 methods. Any help would be greatly appreciated. location: any; doSomethingOne() { Geolocation.getCurrentPosition().then((resp) => { ...

Having trouble configuring Jest for TypeScript integration

I am currently implementing unit testing for a typescript project following the guidelines in this example: https://dev.to/muhajirdev/unit-testing-with-typescript-and-jest-2gln In my project, I have a file named main.ts that exports an isInternalLink func ...

Is there a way to store data in Firebase without generating a unique identifier for each entry?

I am currently facing a challenge with saving my data in the firebase database. The issue lies in the fact that the data is saved with an auto-increment ID, whereas I require a custom ID for each entry. Can someone please provide guidance on how I can achi ...