TinyTiger
TinyTiger

Reputation: 2093

When using Pinia and TypeScript how do you use an Action to set the State?

I have a Pinia + TypeScript store named user.ts that looks like this:

import { User } from 'firebase/auth';
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
  state: () =>
    ({
      displayName: null,
      email: null,
      emailVerified: null,
      isAnonymous: null,
      metadata: null,
      multiFactor: null,
      phoneNumber: null,
      photoURL: null,
      providerData: null,
      providerId: null,
      refreshToken: null,
      tenantId: null,
      uid: null,
    } as unknown as User),
  actions: {
    setPhotoURL(photoURLData: string | null) {
      this.photoURL = photoURLData; //<-- ERROR HERE
    },
  },
});

The state is a FireBase User object.

And I want to update photoURL using the setPhotoURL() action.

But photoURL has this TypeScript error:

Cannot assign to 'photoURL' because it is a read-only property. ts(2540)

What am I doing wrong?

Is this the correct way to update state?

Upvotes: 2

Views: 4608

Answers (2)

tony19
tony19

Reputation: 138526

You can remove the readonly modifier by mapping the type :

Mapping Modifiers

There are two additional modifiers which can be applied during mapping: readonly and ? which affect mutability and optionality respectively.

You can remove or add these modifiers by prefixing with - or +. If you don’t add a prefix, then + is assumed.

// Removes 'readonly' attributes from a type's properties
type CreateMutable<Type> = {
  -readonly [Property in keyof Type]: Type[Property];
};

If the type has nested properties that you want to be writeable, map recursively:

type CreateMutable<T> = { -readonly [P in keyof T]: CreateMutable<T[P]> }

Then use it to type the store's user property:

import type { User } from 'firebase/auth'
import { defineStore } from 'pinia'

type CreateMutable<T> = { -readonly [P in keyof T]: CreateMutable<T[P]> }

export const useUserStore = defineStore('user', {
  state: () => ({ user: {} as CreateMutable<User> }),

  actions: {
    setPhotoURL(photoURLData: string | null) {
      this.user.photoURL = photoURLData
    },
  },
})

Upvotes: 1

tao
tao

Reputation: 90188

'firebase/auth' declares User interface as having readonly props. When you forcefully apply that interface to your state object, TS trusts you.

Spreading it should remove the readonly from its props, while still inferring the types:

export const useUserStore = defineStore('user', {
  state: () => ({ ...({
    displayName: null,
    email: null,
    emailVerified: null,
    isAnonymous: null,
    metadata: null,
    multiFactor: null,
    phoneNumber: null,
    photoURL: null,
    providerData: null,
    providerId: null,
    refreshToken: null,
    tenantId: null,
    uid: null,
  } as unknown as User) }),
  actions: {
    //...
  }
})

Upvotes: 1

Related Questions