使用 Vue3 和 TypeScript 将 Pinia Store 作为 Prop 传递

aro*_*uby 2 typescript vue.js vuejs3 pinia

我正在使用 Typescript、Pinia 和 Vue3,并且有一个MenuButton组件,我希望能够传递用于菜单打开状态以及显示/隐藏操作的 Pinia 存储。应用程序中有几个不同的菜单,因此我希望能够将它们传入,并且它们都使用相同的工厂来定义商店。我正在尝试找出如何让所有这些都与打字稿一起使用。

\n
// nav.store.ts\n\nimport { defineStore } from "pinia";\nimport { useStorage } from "@vueuse/core";\nimport type { RemovableRef } from "@vueuse/core";\n\nexport interface MenuStore {\n    isOpen: RemovableRef<boolean>,\n    toggle(force?: boolean) : void,\n    open(): void,\n    close(): void,\n}\n\ninterface State {\n    isOpen: RemovableRef<boolean>;\n}\n\nfunction menuStoreFactory(id: string) {\n    return defineStore(id, {\n        state: () : State => ({\n            isOpen: useStorage(`${id}-open`, false),\n        }),\n\n        actions: {\n            toggle(force?: boolean) {\n                this.isOpen = force != undefined ? force : !this.isOpen;\n            },\n\n            open() {\n                this.isOpen = true;\n            },\n\n            close() {\n                this.isOpen = false;\n            }\n        }\n    });\n}\n\nexport const useMainMenuStore = menuStoreFactory(\'mainMenu\');\n\nexport const useMobileMenuStore = menuStoreFactory(\'mobileMenu\');\n
Run Code Online (Sandbox Code Playgroud)\n
// setup script for the menu button component\n\nimport { MenuIcon, MenuLeftIcon } from "@/icons";\nimport type { MenuStore } from "@/modules/nav/nav.store";\n\ninterface Props {\n    controller: MenuStore\n}\n\nconst props = defineProps<Props>();\n
Run Code Online (Sandbox Code Playgroud)\n

那么用法就非常简单了:

\n
<template>\n    <MenuButton\n        :controller="mainMenu"\n    ></MenuButton>\n</template>\n<script setup lang=ts">\n    const mainMenu = useMainMenuStore();\n</script>\n
Run Code Online (Sandbox Code Playgroud)\n

在当前界面中,我收到了道具不匹配的错误。经过一番研究,我将界面变成了以下内容,修复了使用错误,但随后在组件中抛出了一个MenuButtontoggle()解决的错误isOpen

\n
export interface MenuStore extends PiniaCustomStateProperties<{\n    isOpen: RemovableRef<boolean>,\n    toggle(force?: boolean) : void,\n    open(): void,\n    close(): void,\n}> {}\n
Run Code Online (Sandbox Code Playgroud)\n

另一个接近的尝试调整是:

\n
export interface MenuStore extends Store<string, {\n    isOpen: RemovableRef<boolean>,\n    toggle(force?: boolean) : void,\n    open(): void,\n    close(): void,\n}> {}\n
Run Code Online (Sandbox Code Playgroud)\n

这导致了使用时出现此错误,但组件中没有错误

\n
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 ... \xc2\xa0\xc2\xa0Type PiniaCustomStateProperties<State> is not assignable to type MenuStore \n
Run Code Online (Sandbox Code Playgroud)\n

Dar*_*kes 5

您的问题似乎主要是关于您需要的类型/接口,所以我会回答这个问题。

您可以简单地使用返回的存储类型,而不是自己编写类型。

您有一个返回 的返回值的工厂函数defineStore,它本身就是一个返回存储的函数。因此,可以使用获取typeof工厂函数的类型来找到存储的类型,然后使用 TypeScript 的ReturnType帮助程序深入了解返回类型。

以下应该是您所需要的:

export type MenuStore = ReturnType<ReturnType<typeof menuStoreFactory>>
Run Code Online (Sandbox Code Playgroud)

分解:

ReturnType<                 // C
  ReturnType<               // B
    typeof menuStoreFactory // A
  >
>
Run Code Online (Sandbox Code Playgroud)
  • A:获取工厂函数的类型。
  • B:获取工厂函数的返回类型,即return from defineStore。这本身就是一个返回存储的函数。
  • C:获取该函数的返回类型。这是商店本身的类型。