From 48523da371ac291689b7a62b9ec433894a595827 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 17 May 2021 18:37:49 +0200 Subject: [PATCH] fix(types): correct subtype Store Close #500 --- src/store.ts | 2 +- src/types.ts | 49 +++++++++++++++++---------------- test-dts/plugins.test-d.ts | 4 +-- test-dts/store.test-d.ts | 56 +++++++++++++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 28 deletions(-) diff --git a/src/store.ts b/src/store.ts index ae1ffe06..88ddf00b 100644 --- a/src/store.ts +++ b/src/store.ts @@ -236,7 +236,7 @@ function initStore< const storeWithState: StoreWithState = { $id, _p: pinia, - _as: actionSubscriptions, + _as: actionSubscriptions as unknown as StoreOnActionListener[], // $state is added underneath diff --git a/src/types.ts b/src/types.ts index 4e99a59e..e4094006 100644 --- a/src/types.ts +++ b/src/types.ts @@ -207,10 +207,10 @@ export type StoreOnActionListenerContext< * Argument of `store.$onAction()` */ export type StoreOnActionListener< - Id extends string, - S extends StateTree, - G extends GettersTree, - A /* extends ActionsTree */ + Id extends string = string, + S extends StateTree = StateTree, + G extends GettersTree = GettersTree, + A /* extends ActionsTree */ = ActionsTree > = (context: StoreOnActionListenerContext) => void /** @@ -220,7 +220,7 @@ export type StoreOnActionListener< export interface StoreWithState< Id extends string, S extends StateTree, - G extends GettersTree = GettersTree, + G extends GettersTree = GettersTree, A /* extends ActionsTree */ = ActionsTree > { /** @@ -231,8 +231,7 @@ export interface StoreWithState< /** * State of the Store. Setting it will replace the whole state. */ - $state: (StateTree extends S ? {} : UnwrapRef) & - PiniaCustomStateProperties + $state: UnwrapRef & PiniaCustomStateProperties /** * Private property defining the pinia the store is attached to. @@ -284,11 +283,12 @@ export interface StoreWithState< $subscribe(callback: SubscriptionCallback): () => 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[] + _as: StoreOnActionListener[] /** * @alpha Please send feedback at https://github.com/posva/pinia/issues/240 @@ -371,7 +371,7 @@ export type Store< G extends GettersTree = GettersTree, // has the actions without the context (this) for typings A /* extends ActionsTree */ = ActionsTree -> = StoreWithState & +> = StoreWithState & (StateTree extends S ? {} : UnwrapRef) & (GettersTree extends G ? {} : StoreWithGetters) & (ActionsTree extends A ? {} : StoreWithActions) & @@ -379,21 +379,22 @@ export type Store< PiniaCustomStateProperties /** - * 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, - ActionsTree -> & - PiniaCustomProperties< - string, - StateTree, - GettersTree, - ActionsTree - > & - PiniaCustomStateProperties +export type GenericStore< + Id extends string = string, + S extends StateTree = StateTree, + G extends GettersTree = GettersTree, + // has the actions without the context (this) for typings + A /* extends ActionsTree */ = ActionsTree +> = StoreWithState & + UnwrapRef & + StoreWithGetters & + StoreWithActions & + PiniaCustomProperties & + PiniaCustomStateProperties /** * Return type of `defineStore()`. Function that allows instantiating a store. diff --git a/test-dts/plugins.test-d.ts b/test-dts/plugins.test-d.ts index 98e4bb19..7d99bb47 100644 --- a/test-dts/plugins.test-d.ts +++ b/test-dts/plugins.test-d.ts @@ -2,7 +2,7 @@ import { App } from 'vue' import { expectType, createPinia, - GenericStore, + Store, Pinia, StateTree, DefineStoreOptions, @@ -11,7 +11,7 @@ import { const pinia = createPinia() pinia.use(({ store, app, options, pinia }) => { - expectType(store) + expectType(store) expectType(pinia) expectType(app) expectType< diff --git a/test-dts/store.test-d.ts b/test-dts/store.test-d.ts index 47bc00ee..62851f58 100644 --- a/test-dts/store.test-d.ts +++ b/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', @@ -118,3 +119,56 @@ noS.notExisting noA.notExisting // @ts-expect-error noG.notExisting + +function takeStore(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(genericStore.thing) +expectType(genericStore.$state.thing) +takeStore(genericStore) +useSyncValueToStore(() => 2, genericStore, 'myState') +useSyncValueToStore(() => 2, genericStore, 'random') +// @ts-expect-error +useSyncValueToStore(() => false, genericStore, 'myState') -- 2.47.2