mapState,
mapStores,
PiniaPlugin,
+ mapWritableState,
setMapStoreSuffix,
} from '../src'
import { createLocalVue, mount } from '@vue/test-utils'
expect(wrapper.vm.set(4)).toBe(4)
})
})
+
+ describe('mapWritableState', () => {
+ async function testComponent(
+ computedProperties: any,
+ template: string,
+ expectedText: string,
+ expectedText2: string
+ ) {
+ const pinia = createPinia()
+ const Component = defineComponent({
+ template: `<p>${template}</p>`,
+ computed: {
+ ...computedProperties,
+ },
+ methods: Object.keys(computedProperties).reduce((methods, name) => {
+ // @ts-ignore
+ methods['set_' + name] = function (v: any) {
+ // @ts-ignore
+ this[name] = v
+ }
+ return methods
+ }, {}),
+ })
+
+ const wrapper = mount(Component, { localVue, pinia })
+
+ expect(wrapper.text()).toBe(expectedText)
+
+ for (const key in computedProperties) {
+ // @ts-ignore
+ wrapper.vm['set_' + key]('replaced')
+ }
+
+ await nextTick()
+
+ expect(wrapper.text()).toBe(expectedText2)
+ }
+
+ it('array', async () => {
+ await testComponent(
+ mapWritableState(useStore, ['n', 'a']),
+ `{{ n }} {{ a }}`,
+ `0 true`,
+ 'replaced replaced'
+ )
+ })
+
+ it('object', async () => {
+ await testComponent(
+ mapWritableState(useStore, { count: 'n', myA: 'a' }),
+ `{{ count }} {{ myA }}`,
+ `0 true`,
+ 'replaced replaced'
+ )
+ })
+ })
})
return reduced
}, {} as MapActionsObjectReturn<A, KeyMapper>)
}
+
+type MapWritableStateReturn<S extends StateTree> = {
+ [key in keyof S]: {
+ get: () => Store<string, S, {}, {}>[key]
+ set: (value: Store<string, S, {}, {}>[key]) => any
+ }
+}
+
+type MapWritableStateObjectReturn<
+ S extends StateTree,
+ T extends Record<string, keyof S>
+> = {
+ [key in keyof T]: {
+ get: () => Store<string, S, {}, {}>[T[key]]
+ set: (value: Store<string, S, {}, {}>[T[key]]) => any
+ }
+}
+
+/**
+ * Same as `mapState()` but creates computed setters as well so the state can be
+ * modified. Differently from `mapState()`, only `state` properties can be
+ * added.
+ *
+ * @param useStore - store to map from
+ * @param keyMapper - object of state properties
+ */
+export function mapWritableState<
+ Id extends string,
+ S extends StateTree,
+ G,
+ A,
+ KeyMapper extends Record<string, keyof S>
+>(
+ useStore: StoreDefinition<Id, S, G, A>,
+ keyMapper: KeyMapper
+): MapWritableStateObjectReturn<S, KeyMapper>
+/**
+ * Allows using state and getters from one store without using the composition
+ * API (`setup()`) by generating an object to be spread in the `computed` field
+ * of a component.
+ *
+ * @param useStore - store to map from
+ * @param keys - array of state properties
+ */
+export function mapWritableState<Id extends string, S extends StateTree, G, A>(
+ useStore: StoreDefinition<Id, S, G, A>,
+ keys: Array<keyof S>
+): MapWritableStateReturn<S>
+/**
+ * Allows using state and getters from one store without using the composition
+ * API (`setup()`) by generating an object to be spread in the `computed` field
+ * of a component.
+ *
+ * @param useStore - store to map from
+ * @param keysOrMapper - array or object
+ */
+export function mapWritableState<
+ Id extends string,
+ S extends StateTree,
+ G,
+ A,
+ KeyMapper extends Record<string, keyof S>
+>(
+ useStore: StoreDefinition<Id, S, G, A>,
+ keysOrMapper: Array<keyof S> | KeyMapper
+): MapWritableStateReturn<S> | MapWritableStateObjectReturn<S, KeyMapper> {
+ return Array.isArray(keysOrMapper)
+ ? keysOrMapper.reduce((reduced, key) => {
+ reduced[key] = {
+ get(this: Vue) {
+ return getCachedStore(this, useStore)[key]
+ },
+ set(this: Vue, value) {
+ // it's easier to type it here as any
+ return (getCachedStore(this, useStore)[key] = value as any)
+ },
+ }
+ return reduced
+ }, {} as MapWritableStateReturn<S>)
+ : Object.keys(keysOrMapper).reduce((reduced, key: keyof KeyMapper) => {
+ // @ts-ignore
+ reduced[key] = {
+ get(this: Vue) {
+ return getCachedStore(this, useStore)[keysOrMapper[key]]
+ },
+ set(this: Vue, value) {
+ // it's easier to type it here as any
+ return (getCachedStore(this, useStore)[
+ keysOrMapper[key]
+ ] = value as any)
+ },
+ }
+ return reduced
+ }, {} as MapWritableStateObjectReturn<S, KeyMapper>)
+}
-import { defineStore, expectType, mapStores, mapActions, mapState } from '.'
+import {
+ defineStore,
+ expectType,
+ mapStores,
+ mapActions,
+ mapState,
+ mapWritableState,
+} from '.'
const useStore = defineStore({
id: 'name',
newSetToggle: (a: 'on' | 'off') => 'on' | 'off'
newToggleA: () => void
}>(mapActions(useStore, { newSetToggle: 'setToggle', newToggleA: 'toggleA' }))
+
+expectType<{
+ a: {
+ get: () => 'on' | 'off'
+ set: (v: 'on' | 'off') => any
+ }
+}>(mapWritableState(useStore, ['a']))
+
+expectType<{
+ newA: {
+ get: () => 'on' | 'off'
+ set: (v: 'on' | 'off') => any
+ }
+}>(mapWritableState(useStore, { newA: 'a' }))
+
+// @ts-expect-error: cannot use a getter
+mapWritableState(useStore, ['upper'])
+// @ts-expect-error: cannot use a getter
+mapWritableState(useStore, { up: 'upper' })