ウェブサイト検索

vue.js - Pinia と TypeScript を使用する場合、Action を使って State を設定するにはどうすればよいですか?


私は user.ts という名前の Pinia + TypeScript ストアを持っており、それは次のようになっています:

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
    },
  },
});

その状態は FireBase のユーザーオブジェクトです。

そして、setPhotoURL() アクションを使って photoURL を更新したいです。

しかし、photoURL にはこの TypeScript エラーがあります:

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

私は何を間違っているのですか?

これは状態を更新する正しい方法ですか?

回答 #1

型をマッピングすることで、readonly 修飾子を削除できます:

修飾子のマッピング

マッピング中に適用できる追加の修飾子が2つあります:readonly と ? です。これらはそれぞれ変更可能性とオプショナル性に影響します。

これらの修飾子は、- または + を前につけることで削除または追加できます。接頭辞を追加しない場合は、+ が想定されます。

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

もし型に書き込み可能にしたいネストしたプロパティがある場合は、再帰的にマッピングします:

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

それから、それを使ってストアのuserプロパティを入力します:

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
    },
  },
})

回答 #2

'firebase/auth' は User インターフェースを readonly プロパティとして宣言しています。そのインターフェースを状態オブジェクトに強制的に適用すると、TS はそれを信頼します。

それを展開すると、型を推論したまま、readonly をそのプロパティから削除するはずです:

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: {
    //...
  }
})