).toEqual(objectOfRefs({ n: 0, pluginN: 20 }))
})
+ it('preserve setters in getters', () => {
+ 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 refs = storeToRefs(useStore())
+ refs.double.value = 4
+ expect(refs.n.value).toBe(2)
+ })
+
tds(() => {
const store1 = defineStore('a', () => {
const n = ref(0)
isReactive,
isRef,
isVue2,
- Ref,
toRaw,
ToRef,
toRef,
ToRefs,
toRefs,
+ WritableComputedRef,
} from 'vue-demi'
import { StoreGetters, StoreState } from './store'
import type {
StoreGeneric,
} from './types'
-type ToComputedRefs<T> = {
- [K in keyof T]: ToRef<T[K]> extends Ref ? ComputedRef<T[K]> : ToRef<T[K]>
+/**
+ * Internal utility type
+ */
+type _IfEquals<X, Y, A = true, B = false> =
+ (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B
+
+/**
+ * Internal utility type
+ */
+type _IsReadonly<T, K extends keyof T> = _IfEquals<
+ { [P in K]: T[P] },
+ { -readonly [P in K]: T[P] },
+ false, // Property is not readonly if they are the same
+ true // Property is readonly if they differ
+>
+
+/**
+ * Extracts the getters of a store while keeping writable and readonly properties. **Internal type DO NOT USE**.
+ */
+type _ToComputedRefs<SS> = {
+ [K in keyof SS]: true extends _IsReadonly<SS, K>
+ ? ComputedRef<SS[K]>
+ : WritableComputedRef<SS[K]>
}
/**
*/
export type StoreToRefs<SS extends StoreGeneric> = _ToStateRefs<SS> &
ToRefs<PiniaCustomStateProperties<StoreState<SS>>> &
- ToComputedRefs<StoreGetters<SS>>
+ _ToComputedRefs<StoreGetters<SS>>
/**
* Creates an object of references with all the state, getters, and plugin-added
*/
actions: A
}
+
+/**
+ * Utility type. For internal use **only**
+ */
+export interface _Empty {}
+
+/**
+ * Merges type objects for better readability in the code.
+ * Utility type. For internal use **only**
+ */
+export type _Simplify<T> = _Empty extends T
+ ? _Empty
+ : { [key in keyof T]: T[key] } & {}
-import { StoreGeneric, acceptHMRUpdate, defineStore, expectType } from './'
+import {
+ StoreGeneric,
+ acceptHMRUpdate,
+ defineStore,
+ expectType,
+ storeToRefs,
+} from './'
import { computed, ref, UnwrapRef, watch } from 'vue'
const useStore = defineStore({
const writableComputedStore = defineStore('computed-writable', () => {
const fruitsBasket = ref(['banana', 'apple', 'banana', 'orange'])
+ const total = computed(() => fruitsBasket.value.length)
const bananasAmount = computed<number>({
get: () => fruitsBasket.value.filter((fruit) => fruit === 'banana').length,
set: (newAmount) => {
)),
})
bananas.value = 'hello' // TS ok
- return { fruitsBasket, bananas, bananasAmount }
+ return { fruitsBasket, bananas, bananasAmount, total }
})()
expectType<number>(writableComputedStore.bananasAmount)
// should allow writing to it
writableComputedStore.bananasAmount = 0
+// @ts-expect-error: this one is readonly
+writableComputedStore.total = 0
expectType<string[]>(writableComputedStore.bananas)
// should allow setting a different type
// @ts-expect-error: still not doable
writableComputedStore.bananas = 'hello'
+
+const refs = storeToRefs(writableComputedStore)
+expectType<string[]>(refs.bananas.value)
+expectType<number>(refs.bananasAmount.value)
+refs.bananasAmount.value = 0
+// @ts-expect-error: this one is readonly
+refs.total.value = 0