diff --git a/packages/pinia/__tests__/mapHelpers.spec.ts b/packages/pinia/__tests__/mapHelpers.spec.ts index 11a5b415f2..9eb9e01fc7 100644 --- a/packages/pinia/__tests__/mapHelpers.spec.ts +++ b/packages/pinia/__tests__/mapHelpers.spec.ts @@ -10,7 +10,7 @@ import { setMapStoreSuffix, } from '../src' import { mount } from '@vue/test-utils' -import { nextTick, defineComponent } from 'vue' +import { nextTick, defineComponent, ref, computed } from 'vue' import { mockWarn } from './vitest-mock-warn' describe('Map Helpers', () => { @@ -245,5 +245,27 @@ describe('Map Helpers', () => { 'replaced replaced' ) }) + + it('setup store', async () => { + const useSetupStore = defineStore('setup', () => { + const text = ref('initial') + + const textUpper = computed({ + get: () => text.value.toUpperCase(), + set: (v) => { + text.value = v + }, + }) + + return { text, textUpper } + }) + + await testComponent( + mapWritableState(useSetupStore, ['text', 'textUpper']), + `{{ text }} {{ textUpper }}`, + `initial INITIAL`, + 'replaced REPLACED' + ) + }) }) }) diff --git a/packages/pinia/src/mapHelpers.ts b/packages/pinia/src/mapHelpers.ts index 4cb219008c..aa7662cb09 100644 --- a/packages/pinia/src/mapHelpers.ts +++ b/packages/pinia/src/mapHelpers.ts @@ -1,7 +1,7 @@ import type { ComponentPublicInstance, ComputedRef, UnwrapRef } from 'vue-demi' import type { _GettersTree, - _Method, + _StoreWithGetters_Writable, StateTree, Store, StoreDefinition, @@ -431,10 +431,21 @@ export function mapActions< /** * For internal use **only** */ -export type _MapWritableStateReturn = { - [key in keyof S]: { - get: () => S[key] - set: (value: S[key]) => any +export type _MapWritableStateKeys = + | keyof UnwrapRef + | keyof _StoreWithGetters_Writable + +/** + * For internal use **only** + */ +export type _MapWritableStateReturn< + S extends StateTree, + G, + Keys extends _MapWritableStateKeys, +> = { + [key in Keys]: { + get: () => UnwrapRef<(S & G)[key]> + set: (value: UnwrapRef<(S & G)[key]>) => any } } @@ -442,12 +453,13 @@ export type _MapWritableStateReturn = { * For internal use **only** */ export type _MapWritableStateObjectReturn< - S, - T extends Record, + S extends StateTree, + G, + KeyMapper extends Record>, > = { - [key in keyof T]: { - get: () => S[T[key]] - set: (value: S[T[key]]) => any + [key in keyof KeyMapper]: { + get: () => UnwrapRef<(S & G)[KeyMapper[key]]> + set: (value: UnwrapRef<(S & G)[KeyMapper[key]]>) => any } } @@ -462,13 +474,13 @@ export type _MapWritableStateObjectReturn< export function mapWritableState< Id extends string, S extends StateTree, - G extends _GettersTree, + G, A, - KeyMapper extends Record>, + KeyMapper extends Record>, >( useStore: StoreDefinition, keyMapper: KeyMapper -): _MapWritableStateObjectReturn, KeyMapper> +): _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 @@ -480,13 +492,13 @@ export function mapWritableState< export function mapWritableState< Id extends string, S extends StateTree, - G extends _GettersTree, + G, A, - Keys extends keyof UnwrapRef, + Keys extends _MapWritableStateKeys, >( useStore: StoreDefinition, keys: readonly Keys[] -): Pick<_MapWritableStateReturn>, Keys> +): Pick<_MapWritableStateReturn, Keys> /** * 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 @@ -498,43 +510,51 @@ export function mapWritableState< export function mapWritableState< Id extends string, S extends StateTree, - G extends _GettersTree, + G, A, - KeyMapper extends Record, + Keys extends _MapWritableStateKeys, + KeyArr extends Keys[], + KeyMapper extends Record, >( useStore: StoreDefinition, - keysOrMapper: Array | KeyMapper -): _MapWritableStateReturn | _MapWritableStateObjectReturn { + keysOrMapper: KeyArr | KeyMapper +): + | _MapWritableStateReturn + | _MapWritableStateObjectReturn { return Array.isArray(keysOrMapper) - ? keysOrMapper.reduce((reduced, key) => { - // @ts-ignore - reduced[key] = { - get(this: ComponentPublicInstance) { - // @ts-expect-error: FIXME: should work? - return useStore(this.$pinia)[key] - }, - set(this: ComponentPublicInstance, value) { - // @ts-expect-error: FIXME: should work? - return (useStore(this.$pinia)[key] = value) - }, - } - return reduced - }, {} as _MapWritableStateReturn) + ? keysOrMapper.reduce( + (reduced, key) => { + reduced[key] = { + get(this: ComponentPublicInstance) { + return useStore(this.$pinia)[key] as (S & G)[typeof key] + }, + set( + this: ComponentPublicInstance, + value: Store[typeof key] + ) { + return (useStore(this.$pinia)[key] = value) + }, + } + return reduced + }, + {} as _MapWritableStateReturn + ) : Object.keys(keysOrMapper).reduce( (reduced, key: keyof KeyMapper) => { - // @ts-ignore reduced[key] = { get(this: ComponentPublicInstance) { - // @ts-expect-error: FIXME: should work? - return useStore(this.$pinia)[keysOrMapper[key]] + return useStore(this.$pinia)[keysOrMapper[key]] as (S & + G)[KeyMapper[typeof key]] }, - set(this: ComponentPublicInstance, value) { - // @ts-expect-error: FIXME: should work? + set( + this: ComponentPublicInstance, + value: Store[KeyMapper[typeof key]] + ) { return (useStore(this.$pinia)[keysOrMapper[key]] = value) }, } return reduced }, - {} as _MapWritableStateObjectReturn + {} as _MapWritableStateObjectReturn ) } diff --git a/packages/pinia/test-dts/mapHelpers.test-d.ts b/packages/pinia/test-dts/mapHelpers.test-d.ts index b9221246ce..1d2ee3cb8a 100644 --- a/packages/pinia/test-dts/mapHelpers.test-d.ts +++ b/packages/pinia/test-dts/mapHelpers.test-d.ts @@ -29,13 +29,17 @@ describe('mapHelpers', () => { const useSetupStore = defineStore('setupStore', () => { const a = ref('on' as 'on' | 'off') const upper = computed(() => a.value.toUpperCase()) + const writableUpper = computed({ + get: () => a.value.toUpperCase(), + set: (v: 'on' | 'off') => (a.value = v), + }) function toggleA() { a.value = a.value === 'off' ? 'on' : 'off' } function setToggle(aVal: 'on' | 'off') { return (a.value = aVal) } - return { a, upper, toggleA, setToggle } + return { a, upper, writableUpper, toggleA, setToggle } }) const useCounter = defineStore({ @@ -161,6 +165,13 @@ describe('mapHelpers', () => { set: (v: 'on' | 'off') => any } }>(mapWritableState(useSetupStore, ['a'])) + + expectTypeOf<{ + writableUpper: { + get: () => string + set: (v: 'on' | 'off') => any + } + }>(mapWritableState(useSetupStore, ['writableUpper'])) }) }) })