From: Eduardo San Martin Morote Date: Thu, 8 Apr 2021 16:22:30 +0000 (+0200) Subject: feat: mapWritableState X-Git-Tag: v2.0.0-alpha.11~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3218bdbb916049bebf1416cef1c103ecda66274b;p=thirdparty%2Fvuejs%2Fpinia.git feat: mapWritableState --- diff --git a/__tests__/mapHelpers.spec.ts b/__tests__/mapHelpers.spec.ts index 1215b591..da442499 100644 --- a/__tests__/mapHelpers.spec.ts +++ b/__tests__/mapHelpers.spec.ts @@ -5,6 +5,7 @@ import { mapGetters, mapState, mapStores, + mapWritableState, setMapStoreSuffix, } from '../src' import { mount } from '@vue/test-utils' @@ -240,4 +241,60 @@ describe('Map Helpers', () => { 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: `

${template}

`, + 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, { global: { plugins: [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' + ) + }) + }) }) diff --git a/src/index.ts b/src/index.ts index 2bddcd00..101dc7d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,7 @@ export { mapActions, mapStores, mapState, + mapWritableState, mapGetters, MapStoresCustomization, setMapStoreSuffix, diff --git a/src/mapHelpers.ts b/src/mapHelpers.ts index 51cab55c..2a367fbe 100644 --- a/src/mapHelpers.ts +++ b/src/mapHelpers.ts @@ -358,6 +358,23 @@ export function mapActions< }, {} as MapActionsObjectReturn) } +type MapWritableStateReturn = { + [key in keyof S]: { + get: () => Store[key] + set: (value: Store[key]) => any + } +} + +type MapWritableStateObjectReturn< + S extends StateTree, + T extends Record +> = { + [key in keyof T]: { + get: () => Store[T[key]] + set: (value: Store[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 @@ -375,7 +392,7 @@ export function mapWritableState< >( useStore: StoreDefinition, keyMapper: KeyMapper -): MapStateObjectReturn +): MapWritableStateObjectReturn /** * 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 @@ -387,7 +404,7 @@ export function mapWritableState< export function mapWritableState( useStore: StoreDefinition, keys: Array -): MapStateReturn +): MapWritableStateReturn /** * 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 @@ -405,24 +422,33 @@ export function mapWritableState< >( useStore: StoreDefinition, keysOrMapper: Array | KeyMapper -): MapStateReturn | MapStateObjectReturn { +): MapWritableStateReturn | MapWritableStateObjectReturn { return Array.isArray(keysOrMapper) ? keysOrMapper.reduce((reduced, key) => { reduced[key] = { get(this: ComponentInstance) { return getCachedStore(this, useStore)[key] }, - set() {}, + set(value) { + // it's easier to type it here as any + return (getCachedStore(this, useStore)[key] = value as any) + }, } return reduced - }, {} as MapStateReturn) + }, {} as MapWritableStateReturn) : Object.keys(keysOrMapper).reduce((reduced, key: keyof KeyMapper) => { + // @ts-ignore reduced[key] = { get(this: ComponentInstance) { return getCachedStore(this, useStore)[keysOrMapper[key]] }, - set() {}, + set(value) { + // it's easier to type it here as any + return (getCachedStore(this, useStore)[ + keysOrMapper[key] + ] = value as any) + }, } return reduced - }, {} as MapStateObjectReturn) + }, {} as MapWritableStateObjectReturn) } diff --git a/test-dts/mapHelpers.test-d.ts b/test-dts/mapHelpers.test-d.ts index 0327ed8a..8b779992 100644 --- a/test-dts/mapHelpers.test-d.ts +++ b/test-dts/mapHelpers.test-d.ts @@ -1,4 +1,11 @@ -import { defineStore, expectType, mapStores, mapActions, mapState } from '.' +import { + defineStore, + expectType, + mapStores, + mapActions, + mapState, + mapWritableState, +} from '.' const useStore = defineStore({ id: 'name', @@ -73,3 +80,22 @@ expectType<{ 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' })