Skip to content

Commit 0fa633e

Browse files
authored
feat: writable computeds to be picked up by mapWritableState (#2847)
1 parent fc2ec76 commit 0fa633e

File tree

3 files changed

+95
-42
lines changed

3 files changed

+95
-42
lines changed

packages/pinia/__tests__/mapHelpers.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
setMapStoreSuffix,
1111
} from '../src'
1212
import { mount } from '@vue/test-utils'
13-
import { nextTick, defineComponent } from 'vue'
13+
import { nextTick, defineComponent, ref, computed } from 'vue'
1414
import { mockWarn } from './vitest-mock-warn'
1515

1616
describe('Map Helpers', () => {
@@ -245,5 +245,27 @@ describe('Map Helpers', () => {
245245
'replaced replaced'
246246
)
247247
})
248+
249+
it('setup store', async () => {
250+
const useSetupStore = defineStore('setup', () => {
251+
const text = ref('initial')
252+
253+
const textUpper = computed({
254+
get: () => text.value.toUpperCase(),
255+
set: (v) => {
256+
text.value = v
257+
},
258+
})
259+
260+
return { text, textUpper }
261+
})
262+
263+
await testComponent(
264+
mapWritableState(useSetupStore, ['text', 'textUpper']),
265+
`{{ text }} {{ textUpper }}`,
266+
`initial INITIAL`,
267+
'replaced REPLACED'
268+
)
269+
})
248270
})
249271
})

packages/pinia/src/mapHelpers.ts

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ComponentPublicInstance, ComputedRef, UnwrapRef } from 'vue-demi'
22
import type {
33
_GettersTree,
4-
_Method,
4+
_StoreWithGetters_Writable,
55
StateTree,
66
Store,
77
StoreDefinition,
@@ -431,23 +431,35 @@ export function mapActions<
431431
/**
432432
* For internal use **only**
433433
*/
434-
export type _MapWritableStateReturn<S> = {
435-
[key in keyof S]: {
436-
get: () => S[key]
437-
set: (value: S[key]) => any
434+
export type _MapWritableStateKeys<S extends StateTree, G> =
435+
| keyof UnwrapRef<S>
436+
| keyof _StoreWithGetters_Writable<G>
437+
438+
/**
439+
* For internal use **only**
440+
*/
441+
export type _MapWritableStateReturn<
442+
S extends StateTree,
443+
G,
444+
Keys extends _MapWritableStateKeys<S, G>,
445+
> = {
446+
[key in Keys]: {
447+
get: () => UnwrapRef<(S & G)[key]>
448+
set: (value: UnwrapRef<(S & G)[key]>) => any
438449
}
439450
}
440451

441452
/**
442453
* For internal use **only**
443454
*/
444455
export type _MapWritableStateObjectReturn<
445-
S,
446-
T extends Record<string, keyof S>,
456+
S extends StateTree,
457+
G,
458+
KeyMapper extends Record<string, _MapWritableStateKeys<S, G>>,
447459
> = {
448-
[key in keyof T]: {
449-
get: () => S[T[key]]
450-
set: (value: S[T[key]]) => any
460+
[key in keyof KeyMapper]: {
461+
get: () => UnwrapRef<(S & G)[KeyMapper[key]]>
462+
set: (value: UnwrapRef<(S & G)[KeyMapper[key]]>) => any
451463
}
452464
}
453465

@@ -462,13 +474,13 @@ export type _MapWritableStateObjectReturn<
462474
export function mapWritableState<
463475
Id extends string,
464476
S extends StateTree,
465-
G extends _GettersTree<S>,
477+
G,
466478
A,
467-
KeyMapper extends Record<string, keyof UnwrapRef<S>>,
479+
KeyMapper extends Record<string, _MapWritableStateKeys<S, G>>,
468480
>(
469481
useStore: StoreDefinition<Id, S, G, A>,
470482
keyMapper: KeyMapper
471-
): _MapWritableStateObjectReturn<UnwrapRef<S>, KeyMapper>
483+
): _MapWritableStateObjectReturn<S, G, KeyMapper>
472484
/**
473485
* Allows using state and getters from one store without using the composition
474486
* API (`setup()`) by generating an object to be spread in the `computed` field
@@ -480,13 +492,13 @@ export function mapWritableState<
480492
export function mapWritableState<
481493
Id extends string,
482494
S extends StateTree,
483-
G extends _GettersTree<S>,
495+
G,
484496
A,
485-
Keys extends keyof UnwrapRef<S>,
497+
Keys extends _MapWritableStateKeys<S, G>,
486498
>(
487499
useStore: StoreDefinition<Id, S, G, A>,
488500
keys: readonly Keys[]
489-
): Pick<_MapWritableStateReturn<UnwrapRef<S>>, Keys>
501+
): Pick<_MapWritableStateReturn<S, G, Keys>, Keys>
490502
/**
491503
* Allows using state and getters from one store without using the composition
492504
* API (`setup()`) by generating an object to be spread in the `computed` field
@@ -498,43 +510,51 @@ export function mapWritableState<
498510
export function mapWritableState<
499511
Id extends string,
500512
S extends StateTree,
501-
G extends _GettersTree<S>,
513+
G,
502514
A,
503-
KeyMapper extends Record<string, keyof S>,
515+
Keys extends _MapWritableStateKeys<S, G>,
516+
KeyArr extends Keys[],
517+
KeyMapper extends Record<string, Keys>,
504518
>(
505519
useStore: StoreDefinition<Id, S, G, A>,
506-
keysOrMapper: Array<keyof S> | KeyMapper
507-
): _MapWritableStateReturn<S> | _MapWritableStateObjectReturn<S, KeyMapper> {
520+
keysOrMapper: KeyArr | KeyMapper
521+
):
522+
| _MapWritableStateReturn<S, G, Keys>
523+
| _MapWritableStateObjectReturn<S, G, KeyMapper> {
508524
return Array.isArray(keysOrMapper)
509-
? keysOrMapper.reduce((reduced, key) => {
510-
// @ts-ignore
511-
reduced[key] = {
512-
get(this: ComponentPublicInstance) {
513-
// @ts-expect-error: FIXME: should work?
514-
return useStore(this.$pinia)[key]
515-
},
516-
set(this: ComponentPublicInstance, value) {
517-
// @ts-expect-error: FIXME: should work?
518-
return (useStore(this.$pinia)[key] = value)
519-
},
520-
}
521-
return reduced
522-
}, {} as _MapWritableStateReturn<S>)
525+
? keysOrMapper.reduce(
526+
(reduced, key) => {
527+
reduced[key] = {
528+
get(this: ComponentPublicInstance) {
529+
return useStore(this.$pinia)[key] as (S & G)[typeof key]
530+
},
531+
set(
532+
this: ComponentPublicInstance,
533+
value: Store<Id, S, G, A>[typeof key]
534+
) {
535+
return (useStore(this.$pinia)[key] = value)
536+
},
537+
}
538+
return reduced
539+
},
540+
{} as _MapWritableStateReturn<S, G, Keys>
541+
)
523542
: Object.keys(keysOrMapper).reduce(
524543
(reduced, key: keyof KeyMapper) => {
525-
// @ts-ignore
526544
reduced[key] = {
527545
get(this: ComponentPublicInstance) {
528-
// @ts-expect-error: FIXME: should work?
529-
return useStore(this.$pinia)[keysOrMapper[key]]
546+
return useStore(this.$pinia)[keysOrMapper[key]] as (S &
547+
G)[KeyMapper[typeof key]]
530548
},
531-
set(this: ComponentPublicInstance, value) {
532-
// @ts-expect-error: FIXME: should work?
549+
set(
550+
this: ComponentPublicInstance,
551+
value: Store<Id, S, G, A>[KeyMapper[typeof key]]
552+
) {
533553
return (useStore(this.$pinia)[keysOrMapper[key]] = value)
534554
},
535555
}
536556
return reduced
537557
},
538-
{} as _MapWritableStateObjectReturn<S, KeyMapper>
558+
{} as _MapWritableStateObjectReturn<S, G, KeyMapper>
539559
)
540560
}

packages/pinia/test-dts/mapHelpers.test-d.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ describe('mapHelpers', () => {
2929
const useSetupStore = defineStore('setupStore', () => {
3030
const a = ref('on' as 'on' | 'off')
3131
const upper = computed(() => a.value.toUpperCase())
32+
const writableUpper = computed({
33+
get: () => a.value.toUpperCase(),
34+
set: (v: 'on' | 'off') => (a.value = v),
35+
})
3236
function toggleA() {
3337
a.value = a.value === 'off' ? 'on' : 'off'
3438
}
3539
function setToggle(aVal: 'on' | 'off') {
3640
return (a.value = aVal)
3741
}
38-
return { a, upper, toggleA, setToggle }
42+
return { a, upper, writableUpper, toggleA, setToggle }
3943
})
4044

4145
const useCounter = defineStore({
@@ -161,6 +165,13 @@ describe('mapHelpers', () => {
161165
set: (v: 'on' | 'off') => any
162166
}
163167
}>(mapWritableState(useSetupStore, ['a']))
168+
169+
expectTypeOf<{
170+
writableUpper: {
171+
get: () => string
172+
set: (v: 'on' | 'off') => any
173+
}
174+
}>(mapWritableState(useSetupStore, ['writableUpper']))
164175
})
165176
})
166177
})

0 commit comments

Comments
 (0)