Search code examples
typescriptvue.jsvueuse

How to correctly type the VueUse useFirestore() wrapper?


I am using this useFirestore() wrapper provided by VueUse.org

And I'm trying to type the user ref it returns with User, like this:

import { User } from 'src/types';
const userId = useUserStore().user.id;
const userDocRef = doc(db, 'users', userId);
const user = useFirestore<User>(userDocRef); // <-- Error shows here

And this is what my User interface looks like:

export interface User {
  id: string;
  userType?: UserType;
  createdAt?: Timestamp;
  email?: string;
  displayName?: string;
  firstName?: string;
  lastName?: string;
  initials?: string;
  passwordUpdatedAt?: Timestamp;
  photoUrl?: string;
  phoneE164Format?: string;
  phoneNationalFormat?: string;
  stripeId?: string;
  stripeLink?: string;
  googleTokens?: {
    access_token: string;
    expiry_date: number;
    id_token: string;
    refresh_token: string;
    token_type: string;
  };
}

This works. The user variable now has intellisense with User properties.

But it also produces this error on userDocRef:

No overload matches this call. Overload 1 of 4, '(maybeDocRef: MaybeRef<DocumentReference>, initialValue?: User | undefined, options?: UseFirestoreOptions | undefined): Ref<...>', gave the following error. Overload 2 of 4, '(maybeDocRef: MaybeRef<Query>, initialValue?: User[] | undefined, options?: UseFirestoreOptions | undefined): Ref<...>', gave the following error.ts(2769)

At the bottom of the useFirestore() docs it has a section named "Type Declarations" which might help. But it is hard for me to fully understand.

I think I am doing the correct thing by passing User as a generic. So I am not sure why I get this error.

Why am I seeing this error and how to fix it?


Solution

  • I figured it out.

    My interface had a required id property that was causing a conflict.

    Here is the full final code:

    const userId = useUserStore().user.id;
    const userDocRef = doc(db, 'users', userId);
    const user = useFirestore<User>(userDocRef);
    
    export interface User {
      id?: string;  // <-- Updated here
      userType?: UserType;
      createdAt?: Timestamp;
      email?: string;
      displayName?: string;
      firstName?: string;
      lastName?: string;
      initials?: string;
      passwordUpdatedAt?: Timestamp;
      photoUrl?: string;
      phoneE164Format?: string;
      phoneNationalFormat?: string;
      stripeId?: string;
      stripeLink?: string;
      googleTokens?: {
        access_token: string;
        expiry_date: number;
        id_token: string;
        refresh_token: string;
        token_type: string;
      };
    }
    

    This answers the question and fixes my immediate problem, but I'm not sure why I need all interface properties to be optional. That seems quite restrictive.

    If anyone knows why please comment below.