I have a Vue3 app with TypeScript and Pinia.
In the _Layout.vue component when I call currentUser.value.hasPermission() I get the error Uncaught (in promise) TypeError: currentUser.value.hasPermission is not a function.
Is there something with refs or pinia stores that would prevent me from calling a function on an object that had been stored in them?
Code below.
// @/stores/main-store.ts
import { defineStore } from "pinia";
import type { IUser } from "@/lib/user";
export const useStore = defineStore({
  id: "main",
  state: () => ({
    currentUser: {} as IUser,
    currentTenant: null as string | null,
    theme: null as string | null,
  }),
});
// @/lib/user.ts
import { getToken } from "@/lib/auth";
export interface IUser {
  id: string;
  tenants: IUserTenant[];
  hasPermission: (
    tenantIdentifier: string|null,
    permission: IUserPermission
  ) => boolean;
}
export interface IUserTenant {
  tenantIdentifier: string;
  tenantName: string;
  permissions: IUserPermission[];
}
export interface IUserPermission {
  permissionZone: PermissionZone;
  permissionType: PermissionType;
}
export enum PermissionZone {
  Me,
  Tenants,
  Users,
  Contacts,
}
export enum PermissionType {
  List,
  Read,
  Create,
  Update,
  Delete,
}
const API_EP = import.meta.env.VITE_API_ENDPOINT;
export class User implements IUser {
  public static async getCurrentUser(): Promise<IUser> {
    const token = await getToken();
    const response = await fetch(`${API_EP}/user`, {
      headers: {
        Authorization: `Bearer ${token}`,
      },
      mode: "cors",
    });
    if (response.ok) {
      return (await response.json()) as User;
    }
    // TODO Define Error response model and parse for message
    throw new Error("Unable to retrieve current user.");
  }
  public id!: string;
  public tenants: IUserTenant[] = [];
  public hasPermission(tenantIdentifier: string | null, permission: IUserPermission): boolean {
    return this.tenants.some(
      (t) =>
        t.tenantIdentifier === tenantIdentifier &&
        t.permissions.some(
          (p) =>
            p.permissionZone === permission.permissionZone &&
            p.permissionType === permission.permissionType
        )
    );
  }
}
// @/views/_Layout.vue - <script> section
import { onMounted, ref } from "vue";
import { useStore } from "@/stores/main-store";
import { storeToRefs } from "pinia";
import { Navigation } from "@/lib/navigation";
import type { INavigationItem } from "@/lib/navigation";
const store = useStore();
const { currentUser, currentTenant } = storeToRefs(store);
const navItems = ref<INavigationItem[]>();
onMounted(async () => {
  navItems.value = Navigation.filter((i) =>
    currentUser.value.hasPermission(
      currentTenant.value,
      i.permission
    )
  );
});
Edit I forgot about where the currentUser gets populated - this is done in the router setup as follows:
// @/router/index.ts
import { createRouter, createWebHistory } from "vue-router";
import Layout from "@/views/_Layout.vue";
import { AuthenticationGuard } from "vue-auth0-plugin";
import { User } from "@/lib/user";
import { useStore } from "@/stores/main-store";
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      name: "default",
      path: "/",
      component: Layout,
      beforeEnter: async (to, from) => {
        // Handle login
        const authed = await AuthenticationGuard(to, from);
        useStore().$patch({
          currentUser: await User.getCurrentUser(),
        });
        if (authed && to.name === "default")
          await router.push({ name: "dashboard" });
        return authed;
      },
      children: [
        {
          name: "dashboard",
          path: "",
          component: () => import("@/views/DashboardPage.vue"),
        },
      ],
    },
  ],
});
export default router;
 
    