Skip to content

Commit

Permalink
feat: writable computeds to be picked up by mapWritableState (#2847)
Browse files Browse the repository at this point in the history
  • Loading branch information
noootwo authored Nov 29, 2024
1 parent fc2ec76 commit 0fa633e
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 42 deletions.
24 changes: 23 additions & 1 deletion packages/pinia/__tests__/mapHelpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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'
)
})
})
})
100 changes: 60 additions & 40 deletions packages/pinia/src/mapHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ComponentPublicInstance, ComputedRef, UnwrapRef } from 'vue-demi'
import type {
_GettersTree,
_Method,
_StoreWithGetters_Writable,
StateTree,
Store,
StoreDefinition,
Expand Down Expand Up @@ -431,23 +431,35 @@ export function mapActions<
/**
* For internal use **only**
*/
export type _MapWritableStateReturn<S> = {
[key in keyof S]: {
get: () => S[key]
set: (value: S[key]) => any
export type _MapWritableStateKeys<S extends StateTree, G> =
| keyof UnwrapRef<S>
| keyof _StoreWithGetters_Writable<G>

/**
* For internal use **only**
*/
export type _MapWritableStateReturn<
S extends StateTree,
G,
Keys extends _MapWritableStateKeys<S, G>,
> = {
[key in Keys]: {
get: () => UnwrapRef<(S & G)[key]>
set: (value: UnwrapRef<(S & G)[key]>) => any
}
}

/**
* For internal use **only**
*/
export type _MapWritableStateObjectReturn<
S,
T extends Record<string, keyof S>,
S extends StateTree,
G,
KeyMapper extends Record<string, _MapWritableStateKeys<S, G>>,
> = {
[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
}
}

Expand All @@ -462,13 +474,13 @@ export type _MapWritableStateObjectReturn<
export function mapWritableState<
Id extends string,
S extends StateTree,
G extends _GettersTree<S>,
G,
A,
KeyMapper extends Record<string, keyof UnwrapRef<S>>,
KeyMapper extends Record<string, _MapWritableStateKeys<S, G>>,
>(
useStore: StoreDefinition<Id, S, G, A>,
keyMapper: KeyMapper
): _MapWritableStateObjectReturn<UnwrapRef<S>, KeyMapper>
): _MapWritableStateObjectReturn<S, G, 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
Expand All @@ -480,13 +492,13 @@ export function mapWritableState<
export function mapWritableState<
Id extends string,
S extends StateTree,
G extends _GettersTree<S>,
G,
A,
Keys extends keyof UnwrapRef<S>,
Keys extends _MapWritableStateKeys<S, G>,
>(
useStore: StoreDefinition<Id, S, G, A>,
keys: readonly Keys[]
): Pick<_MapWritableStateReturn<UnwrapRef<S>>, Keys>
): Pick<_MapWritableStateReturn<S, G, Keys>, 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
Expand All @@ -498,43 +510,51 @@ export function mapWritableState<
export function mapWritableState<
Id extends string,
S extends StateTree,
G extends _GettersTree<S>,
G,
A,
KeyMapper extends Record<string, keyof S>,
Keys extends _MapWritableStateKeys<S, G>,
KeyArr extends Keys[],
KeyMapper extends Record<string, Keys>,
>(
useStore: StoreDefinition<Id, S, G, A>,
keysOrMapper: Array<keyof S> | KeyMapper
): _MapWritableStateReturn<S> | _MapWritableStateObjectReturn<S, KeyMapper> {
keysOrMapper: KeyArr | KeyMapper
):
| _MapWritableStateReturn<S, G, Keys>
| _MapWritableStateObjectReturn<S, G, KeyMapper> {
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<S>)
? keysOrMapper.reduce(
(reduced, key) => {
reduced[key] = {
get(this: ComponentPublicInstance) {
return useStore(this.$pinia)[key] as (S & G)[typeof key]
},
set(
this: ComponentPublicInstance,
value: Store<Id, S, G, A>[typeof key]
) {
return (useStore(this.$pinia)[key] = value)
},
}
return reduced
},
{} as _MapWritableStateReturn<S, G, Keys>
)
: 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<Id, S, G, A>[KeyMapper[typeof key]]
) {
return (useStore(this.$pinia)[keysOrMapper[key]] = value)
},
}
return reduced
},
{} as _MapWritableStateObjectReturn<S, KeyMapper>
{} as _MapWritableStateObjectReturn<S, G, KeyMapper>
)
}
13 changes: 12 additions & 1 deletion packages/pinia/test-dts/mapHelpers.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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']))
})
})
})

0 comments on commit 0fa633e

Please sign in to comment.