]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
feat: mapWritableState
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 8 Apr 2021 16:22:30 +0000 (18:22 +0200)
committerEduardo San Martin Morote <posva@users.noreply.github.com>
Fri, 9 Apr 2021 11:08:56 +0000 (13:08 +0200)
__tests__/mapHelpers.spec.ts
src/index.ts
src/mapHelpers.ts
test-dts/mapHelpers.test-d.ts

index 1215b591359fbbdde98f39db3da40b25c2679b78..da44249969a88a987e38f655d1ef025f735af4be 100644 (file)
@@ -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: `<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, { 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'
+      )
+    })
+  })
 })
index 2bddcd00357546df3c55f5395771e483504a1147..101dc7d4bb6a4a23a6f4d4f9c669bd1e50653ff1 100644 (file)
@@ -19,6 +19,7 @@ export {
   mapActions,
   mapStores,
   mapState,
+  mapWritableState,
   mapGetters,
   MapStoresCustomization,
   setMapStoreSuffix,
index 51cab55cf16a1b576cb1ace862b83ed117fafda9..2a367fbe6a7789d4b98ad542047eb4e648390953 100644 (file)
@@ -358,6 +358,23 @@ export function mapActions<
       }, {} 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
@@ -375,7 +392,7 @@ export function mapWritableState<
 >(
   useStore: StoreDefinition<Id, S, G, A>,
   keyMapper: KeyMapper
-): MapStateObjectReturn<Id, S, G, A, 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
@@ -387,7 +404,7 @@ export function mapWritableState<
 export function mapWritableState<Id extends string, S extends StateTree, G, A>(
   useStore: StoreDefinition<Id, S, G, A>,
   keys: Array<keyof S>
-): MapStateReturn<S, G>
+): 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
@@ -405,24 +422,33 @@ export function mapWritableState<
 >(
   useStore: StoreDefinition<Id, S, G, A>,
   keysOrMapper: Array<keyof S> | KeyMapper
-): MapStateReturn<S, G> | MapStateObjectReturn<Id, S, G, A, KeyMapper> {
+): MapWritableStateReturn<S> | MapWritableStateObjectReturn<S, KeyMapper> {
   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<S, G>)
+      }, {} as MapWritableStateReturn<S>)
     : 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<Id, S, G, A, KeyMapper>)
+      }, {} as MapWritableStateObjectReturn<S, KeyMapper>)
 }
index 0327ed8a27fb22cd97080117e22c436104b5330b..8b77999268e4e68c2f3efba8ef8d14bd2297b3bf 100644 (file)
@@ -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' })