Pass Pinia Store as Prop using Vue3 and TypeScript
P粉899950720
2023-08-25 18:40:12
<p>I'm using Typescript, Pinia and Vue3, and have a <code>MenuButton</code> component and I want to be able to pass the Pinia storage/hide for the menu open state and the action to show. There are a few different menus in the application so I want to be able to pass them in and have them all use the same factory to define the store. I'm trying to figure out how to get all of this to work with typescript. </p>
<pre class="lang-js prettyprint-override"><code>// 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');
</code></pre>
<pre class="lang-js prettyprint-override"><code>// 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>();
</code></pre>
<p>Then usage is very simple:</p>
<pre class="lang-html prettyprint-override"><code><template>
<MenuButton
:controller="mainMenu"
></MenuButton>
</template>
<script setup lang=ts">
const mainMenu = useMainMenuStore();
</script>
</code></pre>
<p>In the current interface, I receive a prop mismatch error. After some research, I changed the interface to the following, which fixed the usage error, but then threw <code>toggle()</code> and < in the <code>MenuButton</code> component The error /code> for code>isOpen< has not been resolved.</p>
<pre class="lang-js prettyprint-override"><code>export interface MenuStore extends PiniaCustomStateProperties<{
isOpen: RemovableRef<boolean>,
toggle(force?: boolean) : void,
open(): void,
close(): void,
}> {}
</code></pre>
<p>另一个接近的尝试调整是:</p>
<pre class="lang-js prettyprint-override"><code>export interface MenuStore extends Store<string, {
isOpen: RemovableRef<boolean>,
toggle(force?: boolean) : void,
open(): void,
close(): void,
}> {}
</code></pre>
<p>这导致了使用时出现此错误,但组件中没有错误</p>
<pre class="brush:php;toolbar:false;">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</pre></p>
Your question seems to be mostly about the types/interfaces you need, so I'll answer that.
You can simply use the returned stored type instead of writing the type yourself.
You have a factory function that returns the return value of
defineStore
, which is itself a function that returns a store. Therefore, you can usetypeof
to get the type of the factory function, and then use TypeScript'sReturnType
helper to drill down into the return type to find the stored type.The following should be what you need:
Segmentation:
defineStore
. This itself is a function that returns storage.