Utilizing TypeScript with Vue3 to Pass a Pinia Store as a Prop

My current stack includes Typescript, Pinia, and Vue3. I have a MenuButton component that I want to be able to pass a Pinia store for managing the menu open state and related actions. There are multiple menus in the application, each using the same store factory method to define the stores. I am trying to make this setup work seamlessly with Typescript.

// nav.store.ts

import { defineStore } from "pinia";
import { useStorage } from "@vueuse/core";
import type { RemovableRef } from "@vueuse/core";

export interface MenuStore {
    isOpen: RemovableRef<boolean>,
    toggle(force?: boolean) : void,
    open(): void,
    close(): void,
}

interface State {
    isOpen: RemovableRef<boolean>;
}

function menuStoreFactory(id: string) {
    return defineStore(id, {
        state: () : State => ({
            isOpen: useStorage(`${id}-open`, false),
        }),

        actions: {
            toggle(force?: boolean) {
                this.isOpen = force != undefined ? force : !this.isOpen;
            },

            open() {
                this.isOpen = true;
            },

            close() {
                this.isOpen = false;
            }
        }
    });
}

export const useMainMenuStore = menuStoreFactory('mainMenu');

export const useMobileMenuStore = menuStoreFactory('mobileMenu');
// setup script for the menu button component

import { MenuIcon, MenuLeftIcon } from "@/icons";
import type { MenuStore } from "@/modules/nav/nav.store";

interface Props {
    controller: MenuStore
}

const props = defineProps<Props>();

Using the store is straightforward:

<template>
    <MenuButton
        :controller="mainMenu"
    ></MenuButton>
</template>
<script setup lang=ts">
    const mainMenu = useMainMenuStore();
</script>

Initially, there was an error regarding mismatched props. After tweaking the interface as follows, the usage error was fixed, but then another error occurred in the MenuButton component indicating that toggle() and isOpen were unresolved.

export interface MenuStore extends PiniaCustomStateProperties<{
    isOpen: RemovableRef<boolean>,
    toggle(force?: boolean) : void,
    open(): void,
    close(): void,
}> {}

Another attempt involved the following tweak:

export interface MenuStore extends Store<string, {
    isOpen: RemovableRef<boolean>,
    toggle(force?: boolean) : void,
    open(): void,
    close(): void,
}> {}

This resulted in an error upon usage, though no error in the component itself.

Type _StoreWithState<string, State, {}, {toggle(force?: boolean): void, close(): void, open(): void}> & UnwrapRef<State> & _StoreWithGetters<{}> & {toggle(force?: boolean): void, close(): void, open(): void} & PiniaCustomProperties<string, State, {}, {toggle(force?: boolean): void, close(): void, open(): void}> & PiniaCustomStateProperties<State> is not assignable to type MenuStore ...   Type PiniaCustomStateProperties<State> is not assignable to type MenuStore 

Answer №1

If you're wondering about the type or interface needed for your question, I've got you covered.

You don't have to manually write out a type - just use the type of the store that is returned.

By using a factory function that returns the value from defineStore, which in turn returns the store, you can determine the type of the store by utilizing typeof and TypeScript's ReturnType.

All you need is this simple line:

export type MenuStore = ReturnType<ReturnType<typeof menuStoreFactory>>

Here's the breakdown:

ReturnType<                 // C
  ReturnType<               // B
    typeof menuStoreFactory // A
  >
>
  • A: Obtain the type of the factory function.
  • B: Retrieve the return type of the factory function, which is the result of defineStore. This function itself returns the store.
  • C: Obtain the return type of that particular function, representing the store's type.

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

What is the best way to display the complete text or wrap a menu item in an Angular Material menu?

Is it possible to display the full text of a menu item instead of automatically converting it to ellipses or breaking the word? I've tried various CSS methods without success. Any suggestions? https://i.stack.imgur.com/3l7gE.png #html code <mat-m ...

Working with Angular: Managing an Array of Objects

After retrieving an array of objects using the code snippet below: this.serviceOne.getRemoteData().subscribe( data => this.MyArray.push(data) ); I encounter issues when trying to iterate through the array using the code snippet bel ...

Exploring the different routing options for Nuxt.js: Dynamic versus Basic Routing

Can anyone suggest the best way to set up routing in Next.js for only Home, Gallery, and Contact us? Should I use dynamic routing or just keep it basic? Any ideas on how to path them? I'm still learning, so I would appreciate some guidance. I've ...

Issue with hardhat failing to detect balance transfer from smart contract

I am currently in the process of creating tests for a smart contract that I have developed. Below is a simplified version of the smart contract: Please Note: I have hard-coded the value to ensure accuracy regarding the amount leaving the contract. functio ...

The argument of type 'NextRouter' cannot be assigned to the parameter of type 'Props' in this scenario

In my component, I am initializing a Formik form by calling a function and passing the next/router object. This is how it looks: export default function Reset() { const router = useRouter(); const formik = useFormik(RecoverForm(router)); return ( ...

The i18n feature in Nuxt 3 retrieves language locales from an external API

While working on my Nuxt 3 app, I encountered an issue when trying to integrate i18n. Despite conducting extensive research, I couldn't find any helpful information, hence I have a question. I am utilizing i18n with Prismic CMS. The locales array is s ...

Vue.js 2 view failing to update after modifying data in Vuex state

Greetings, I am currently working on developing a table to monitor the validation status of multiple items. Below is the VueX store configuration: mutations: { ..., set_scaninfos: function (state, scaninfos) { Vue.set(state, 'scaninfos&a ...

A comprehensive guide on utilizing the loading.tsx file in Next JS

In the OnboardingForm.tsx component, I have a straightforward function to handle form data. async function handleFormData(formData: FormData) { const result = await createUserFromForm( formData, clerkUserId as string, emailAddress a ...

Angular - Error: Object returned from response does not match the expected type of 'request?: HttpRequest<any>'

While working on implementing an AuthGuard in Angular, I encountered the following Error: Type 'typeof AuthServiceService' is not assignable to type '(request?: HttpRequest) => string | Promise'. Type 'typeof AuthServiceServic ...

Typescript: Issue encountered with Record type causing Type Error

When utilizing handler functions within an object, the Record type is used in the following code snippet: interface User { id: string; avatar: string; email: string; name: string; role?: string; [key: string]: any; } interface Stat ...

Using the parameter value as a property name in the return type of a function in TypeScript: a guide

After creating a function that converts an object to an array where each element contains the ID of the object, I encountered a new requirement. The current function works great with the following code: const objectToArray = <T>(object: { [id: string ...

Utilizing a Typescript class interface does not maintain the original method types

Struggling to define a Typescript interface and implement it in a class. The issue lies in the method signatures of the interface not being applied to the class as expected. Below is a simplified example: export interface Foo { bar(value: string): voi ...

List out the decorators

One query is bothering me - I am attempting to create my own version of Injectable and I need to determine if a specific decorator exists in my Class. Is there a way to list all decorators of a class? Let's take the example below. All I want to know i ...

The vue-test-utils setData method is failing to trigger a re-render of the component

Currently, I am utilizing vue-test-utils in conjunction with jest. In my Login Component, I have a router-link being displayed. Additionally, I am passing the router to the component. Within this context, there exists data named 'email', and whe ...

Setting up Auth0 redirect URL for Vue/Quasar and Capacitor hybrid application

I'm facing some challenges setting up Auth0 for my Quasar app with Capacitor integration. Although auth0-lock works properly, I am unsure how to handle callback URLs when the app is running on a device with Capacitor. For instance, providing a callbac ...

Error! There seems to be a missing start script for Vue.js in the npm ERR

After trying to install Vuetable with the command npm install vuetable-2@next --save, I encountered the following error message: npm ERR! missing script: start npm ERR! A complete log of this run can be found in: npm ERR! x\AppData\Roaming& ...

Could you explain the significance of the typscript parameters encapsulated within curly braces?

I'm confused about the syntax in this TypeScript code snippet. Why is the data parameter enclosed in curly braces and followed by a colon and the same data object with a type specification? Can someone explain what this means? addArrivingTruckSuggesti ...

Building a React TypeScript project is successful on Windows, but encounters issues when attempted on a

Currently, I am immersed in a project that involves React TypeScript. Here is the content of the package.json: { "version": "0.1.0", "private": true, "dependencies": { ///... "react": "^16.8.6", "react-scripts-ts": "3.1.0", }, "scri ...

Is it possible to inject a descendant component's ancestor of the same type using

When working with Angular's dependency injection, it is possible to inject any ancestor component. For example: @Component({ ... }) export class MyComponent { constructor(_parent: AppComponent) {} } However, in my particular scenario, I am tryin ...

v-select/Vue: Can I input a value that is not in the dropdown list?

I'm looking for a solution to include a new value in v-select that is not already in the list. I want the newly entered value to be selected as an option. Below is my existing code: <v-select ref="systemSelect" v-model="reservation.system" ...