expect(store.upperCaseName).toBe('ED')
})
+ it('can use getters with setters', () => {
+ const useStore = defineStore('main', () => {
+ const name = ref('Eduardo')
+ const upperCaseName = computed({
+ get() {
+ return name.value.toUpperCase()
+ },
+ set(value: string) {
+ store.name = value.toLowerCase()
+ },
+ })
+ return { name, upperCaseName }
+ })
+
+ const store = useStore()
+ expect(store.upperCaseName).toBe('EDUARDO')
+ store.upperCaseName = 'ED'
+ expect(store.name).toBe('ed')
+ })
+
+ it('can use getters with setters with different types', () => {
+ const useStore = defineStore('main', () => {
+ const n = ref(0)
+ const double = computed({
+ get() {
+ return n.value * 2
+ },
+ set(value: string | number) {
+ n.value =
+ (typeof value === 'string' ? parseInt(value) || 0 : value) / 2
+ },
+ })
+ return { n, double }
+ })
+
+ const store = useStore()
+ store.double = 4
+ expect(store.n).toBe(2)
+ // @ts-expect-error: still not doable
+ store.double = '6'
+ expect(store.n).toBe(3)
+ })
+
describe('cross used stores', () => {
const useA = defineStore('a', () => {
const B = useB()
DefineStoreOptionsInPlugin,
StoreGeneric,
_StoreWithGetters,
+ _StoreWithGetters_Readonly,
+ _StoreWithGetters_Writable,
_ExtractActionsFromSetupStore,
_ExtractGettersFromSetupStore,
_ExtractStateFromSetupStore,
Ref,
UnwrapRef,
WatchOptions,
+ WritableComputedRef,
} from 'vue-demi'
import { Pinia } from './rootStore'
* Store augmented with getters. For internal usage only.
* For internal use **only**
*/
-export type _StoreWithGetters<G> = {
- readonly [k in keyof G]: G[k] extends (...args: any[]) => infer R
- ? R
- : UnwrapRef<G[k]>
+export type _StoreWithGetters<G> = _StoreWithGetters_Readonly<G> &
+ _StoreWithGetters_Writable<G>
+
+/**
+ * Store augmented with readonly getters. For internal usage **only**.
+ */
+export type _StoreWithGetters_Readonly<G> = {
+ readonly [K in keyof G as G[K] extends (...args: any[]) => any
+ ? K
+ : ComputedRef extends G[K]
+ ? K
+ : never]: G[K] extends (...args: any[]) => infer R ? R : UnwrapRef<G[K]>
+}
+
+/**
+ * Store augmented with writable getters. For internal usage **only**.
+ */
+export type _StoreWithGetters_Writable<G> = {
+ [K in keyof G as G[K] extends WritableComputedRef<any>
+ ? K
+ : // NOTE: there is still no way to have a different type for a setter and a getter in TS with dynamic keys
+ // https://github.com/microsoft/TypeScript/issues/43826
+ never]: G[K] extends WritableComputedRef<infer R, infer _S> ? R : never
}
/**
import { StoreGeneric, acceptHMRUpdate, defineStore, expectType } from './'
-import { UnwrapRef, watch } from 'vue'
+import { computed, ref, UnwrapRef, watch } from 'vue'
const useStore = defineStore({
id: 'name',
export const useSyncValueToStore = <
TStore extends StoreGeneric,
- TKey extends keyof TStore['$state']
+ TKey extends keyof TStore['$state'],
>(
propGetter: () => TStore[TKey],
store: TStore,
// @ts-expect-error: this type is known so it should yield an error
useSyncValueToStore(() => false, genericStore, 'myState')
useSyncValueToStore(() => 2, genericStore, 'random')
+
+const writableComputedStore = defineStore('computed-writable', () => {
+ const fruitsBasket = ref(['banana', 'apple', 'banana', 'orange'])
+ const bananasAmount = computed<number>({
+ get: () => fruitsBasket.value.filter((fruit) => fruit === 'banana').length,
+ set: (newAmount) => {
+ fruitsBasket.value = fruitsBasket.value.filter(
+ (fruit) => fruit !== 'banana'
+ )
+ fruitsBasket.value.push(...Array(newAmount).fill('banana'))
+ },
+ })
+ const bananas = computed({
+ get: () => fruitsBasket.value.filter((fruit) => fruit === 'banana'),
+ set: (newFruit: string) =>
+ (fruitsBasket.value = fruitsBasket.value.map((fruit) =>
+ fruit === 'banana' ? newFruit : fruit
+ )),
+ })
+ bananas.value = 'hello' // TS ok
+ return { fruitsBasket, bananas, bananasAmount }
+})()
+
+expectType<number>(writableComputedStore.bananasAmount)
+// should allow writing to it
+writableComputedStore.bananasAmount = 0
+expectType<string[]>(writableComputedStore.bananas)
+// should allow setting a different type
+// @ts-expect-error: still not doable
+writableComputedStore.bananas = 'hello'