Skip to content

Commit

Permalink
fix(types): correct subtype Store
Browse files Browse the repository at this point in the history
Close #500
  • Loading branch information
posva committed May 17, 2021
1 parent befc132 commit 48523da
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/store.ts
Expand Up @@ -236,7 +236,7 @@ function initStore<
const storeWithState: StoreWithState<Id, S, G, A> = {
$id,
_p: pinia,
_as: actionSubscriptions,
_as: actionSubscriptions as unknown as StoreOnActionListener[],

// $state is added underneath

Expand Down
49 changes: 25 additions & 24 deletions src/types.ts
Expand Up @@ -207,10 +207,10 @@ export type StoreOnActionListenerContext<
* Argument of `store.$onAction()`
*/
export type StoreOnActionListener<
Id extends string,
S extends StateTree,
G extends GettersTree<S>,
A /* extends ActionsTree */
Id extends string = string,
S extends StateTree = StateTree,
G extends GettersTree<S> = GettersTree<S>,
A /* extends ActionsTree */ = ActionsTree
> = (context: StoreOnActionListenerContext<Id, S, G, A>) => void

/**
Expand All @@ -220,7 +220,7 @@ export type StoreOnActionListener<
export interface StoreWithState<
Id extends string,
S extends StateTree,
G extends GettersTree<S> = GettersTree<S>,
G extends GettersTree<StateTree> = GettersTree<S>,
A /* extends ActionsTree */ = ActionsTree
> {
/**
Expand All @@ -231,8 +231,7 @@ export interface StoreWithState<
/**
* State of the Store. Setting it will replace the whole state.
*/
$state: (StateTree extends S ? {} : UnwrapRef<S>) &
PiniaCustomStateProperties<S>
$state: UnwrapRef<S> & PiniaCustomStateProperties<S>

/**
* Private property defining the pinia the store is attached to.
Expand Down Expand Up @@ -284,11 +283,12 @@ export interface StoreWithState<
$subscribe(callback: SubscriptionCallback<S>): () => void

/**
* Array of registered action subscriptions.
* Array of registered action subscriptions.Set without the generics to avoid
* errors between the generic version of Store and specific stores.
*
* @internal
*/
_as: StoreOnActionListener<Id, S, G, A>[]
_as: StoreOnActionListener[]

/**
* @alpha Please send feedback at https://github.com/posva/pinia/issues/240
Expand Down Expand Up @@ -371,29 +371,30 @@ export type Store<
G extends GettersTree<S> = GettersTree<S>,
// has the actions without the context (this) for typings
A /* extends ActionsTree */ = ActionsTree
> = StoreWithState<Id, S, G, A> &
> = StoreWithState<Id, StateTree extends S ? {} : S, G, A> &
(StateTree extends S ? {} : UnwrapRef<S>) &
(GettersTree<S> extends G ? {} : StoreWithGetters<G>) &
(ActionsTree extends A ? {} : StoreWithActions<A>) &
PiniaCustomProperties<Id, S, G, A> &
PiniaCustomStateProperties<S>

/**
* Generic version of Store. Doesn't fail on access with strings
* Generic and type-unsafe version of Store. Doesn't fail on access with
* strings, making it much easier to write generic functions that do not care
* about the kind of store that is passed.
*/
export type GenericStore = StoreWithState<
string,
StateTree,
GettersTree<StateTree>,
ActionsTree
> &
PiniaCustomProperties<
string,
StateTree,
GettersTree<StateTree>,
ActionsTree
> &
PiniaCustomStateProperties<StateTree>
export type GenericStore<
Id extends string = string,
S extends StateTree = StateTree,
G extends GettersTree<S> = GettersTree<S>,
// has the actions without the context (this) for typings
A /* extends ActionsTree */ = ActionsTree
> = StoreWithState<Id, S, G, A> &
UnwrapRef<S> &
StoreWithGetters<G> &
StoreWithActions<A> &
PiniaCustomProperties<Id, S, G, A> &
PiniaCustomStateProperties<S>

/**
* Return type of `defineStore()`. Function that allows instantiating a store.
Expand Down
4 changes: 2 additions & 2 deletions test-dts/plugins.test-d.ts
Expand Up @@ -2,7 +2,7 @@ import { App } from 'vue'
import {
expectType,
createPinia,
GenericStore,
Store,
Pinia,
StateTree,
DefineStoreOptions,
Expand All @@ -11,7 +11,7 @@ import {
const pinia = createPinia()

pinia.use(({ store, app, options, pinia }) => {
expectType<GenericStore>(store)
expectType<Store>(store)
expectType<Pinia>(pinia)
expectType<App>(app)
expectType<
Expand Down
56 changes: 55 additions & 1 deletion test-dts/store.test-d.ts
@@ -1,4 +1,5 @@
import { defineStore, expectType } from './'
import { watch } from '@vue/runtime-core'
import { defineStore, expectType, Store, GenericStore } from './'

const useStore = defineStore({
id: 'name',
Expand Down Expand Up @@ -118,3 +119,56 @@ noS.notExisting
noA.notExisting
// @ts-expect-error
noG.notExisting

function takeStore<TStore extends Store>(store: TStore): TStore['$id'] {
return store.$id
}

export const useSyncValueToStore = <
TStore extends Store,
TKey extends keyof TStore['$state']
>(
propGetter: () => TStore[TKey],
store: TStore,
key: TKey
): void => {
watch(
propGetter,
(propValue) => {
store[key] = propValue
},
{
immediate: true,
}
)
}

useSyncValueToStore(() => 'on' as const, store, 'a')
// @ts-expect-error
useSyncValueToStore(() => true, store, 'a')
takeStore(store)
takeStore(noSAG)
// @ts-expect-error
useSyncValueToStore(() => 2, noSAG, 'nope')
// @ts-expect-error
useSyncValueToStore(() => null, noSAG, 'myState')
takeStore(noSA)
takeStore(noAG)
useSyncValueToStore(() => 2, noAG, 'myState')
takeStore(noSG)
takeStore(noS)
takeStore(noA)
useSyncValueToStore(() => 2, noA, 'myState')
takeStore(noG)
useSyncValueToStore(() => 2, noG, 'myState')

declare var genericStore: GenericStore

// should not fail like it does with Store
expectType<any>(genericStore.thing)
expectType<any>(genericStore.$state.thing)
takeStore(genericStore)
useSyncValueToStore(() => 2, genericStore, 'myState')
useSyncValueToStore(() => 2, genericStore, 'random')
// @ts-expect-error
useSyncValueToStore(() => false, genericStore, 'myState')

0 comments on commit 48523da

Please sign in to comment.