Skip to content

Commit

Permalink
fix(types): fix storeToRefs state return type (#2574) (#2604)
Browse files Browse the repository at this point in the history
* fix(types): Added new mapped type to keep original ref type in the storeToRefs return type (close #2574)

* test: add types test with custom ref type

* test: add type test for an options store that returns custom ref within the state function

* fix: Fix case when store has actions

* test: add type tests for stores with actions
  • Loading branch information
nkeyy0 authored Apr 2, 2024
1 parent 205e3df commit c8f727a
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 12 deletions.
33 changes: 29 additions & 4 deletions packages/pinia/src/storeToRefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,46 @@ import {
toRefs,
} from 'vue-demi'
import { StoreGetters, StoreState } from './store'
import type { PiniaCustomStateProperties, StoreGeneric } from './types'
import type {
_ActionsTree,
_GettersTree,
_UnwrapAll,
PiniaCustomStateProperties,
StateTree,
Store,
StoreGeneric,
} from './types'

type ToComputedRefs<T> = {
[K in keyof T]: ToRef<T[K]> extends Ref<infer U>
? ComputedRef<U>
: ToRef<T[K]>
}

/**
* Extracts the refs of a state object from a store. If the state value is a Ref or type that extends ref, it will be kept as is.
* Otherwise, it will be converted into a Ref.
*/
declare type ToStateRefs<SS> =
SS extends Store<
string,
infer UnwrappedState,
_GettersTree<StateTree>,
_ActionsTree
>
? UnwrappedState extends _UnwrapAll<Pick<infer State, infer Key>>
? {
[K in Key]: ToRef<State[K]>
}
: ToRefs<UnwrappedState>
: ToRefs<StoreState<SS>>

/**
* Extracts the return type for `storeToRefs`.
* Will convert any `getters` into `ComputedRef`.
*/
export type StoreToRefs<SS extends StoreGeneric> = ToRefs<
StoreState<SS> & PiniaCustomStateProperties<StoreState<SS>>
> &
export type StoreToRefs<SS extends StoreGeneric> = ToStateRefs<SS> &
ToRefs<PiniaCustomStateProperties<StoreState<SS>>> &
ToComputedRefs<StoreGetters<SS>>

/**
Expand Down
117 changes: 109 additions & 8 deletions packages/pinia/test-dts/customizations.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,16 @@ expectType<{
pinia.use(({ options, store }) => {
const { debounce: debounceOptions } = options
if (debounceOptions) {
return Object.keys(debounceOptions).reduce((debouncedActions, action) => {
debouncedActions[action] = debounce(
store[action],
debounceOptions[action]
)
return debouncedActions
}, {} as Record<string, (...args: any[]) => any>)
return Object.keys(debounceOptions).reduce(
(debouncedActions, action) => {
debouncedActions[action] = debounce(
store[action],
debounceOptions[action]
)
return debouncedActions
},
{} as Record<string, (...args: any[]) => any>
)
}
})

Expand Down Expand Up @@ -185,8 +188,106 @@ expectType<{
const double = computed(() => n.value * 2)
return {
n,
double
double,
}
})()
)
)

expectType<{
n: Ref<number>
customN: Ref<number> & { plusOne: () => void }
double: ComputedRef<number>
myState: Ref<number>
stateOnly: Ref<number>
}>(
storeToRefs(
defineStore('a', () => {
const n = ref(1)
const customN = ref(1) as Ref<number> & { plusOne: () => void }
const double = computed(() => n.value * 2)
return {
n,
customN,
double,
}
})()
)
)

expectType<{
n: Ref<number>
customN: Ref<number> & { plusOne: () => void }
double: ComputedRef<number>
myState: Ref<number>
stateOnly: Ref<number>
}>(
storeToRefs(
defineStore('a', () => {
const n = ref(1)
const customN = ref(1) as Ref<number> & { plusOne: () => void }
const double = computed(() => n.value * 2)

function plusOne() {
customN.value++
}

return {
n,
customN,
double,
plusOne,
}
})()
)
)

expectType<{
n: Ref<number>
customN: Ref<number> & { plusOne: () => void }
double: ComputedRef<number>
myState: Ref<number>
stateOnly: Ref<number>
}>(
storeToRefs(
defineStore('a', {
state: () => ({
n: 1,
customN: ref(1) as Ref<number> & { plusOne: () => void },
}),
getters: {
double: (state) => state.n * 2,
},
actions: {
plusOne() {
this.n++
},
},
})()
)
)

expectType<{
n: Ref<number>
customN: Ref<number> & { plusOne: () => void }
double: ComputedRef<number>
myState: Ref<number>
stateOnly: Ref<number>
}>(
storeToRefs(
defineStore('a', {
state: () => ({
n: 1,
customN: ref(1) as Ref<number> & { plusOne: () => void },
}),
getters: {
double: (state) => state.n * 2,
},
actions: {
plusOne() {
this.n++
},
},
})()
)
)

0 comments on commit c8f727a

Please sign in to comment.