]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
fix(types): correct subtype Store
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 17 May 2021 16:37:49 +0000 (18:37 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 17 May 2021 16:37:49 +0000 (18:37 +0200)
Close #500

src/store.ts
src/types.ts
test-dts/plugins.test-d.ts
test-dts/store.test-d.ts

index ae1ffe064e241621d2ce145243477fa0b99eff88..88ddf00b888b601240f9173743ca4ef5c49b1916 100644 (file)
@@ -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
 
index 4e99a59eef55b943611f1751ee898abdeffb0ebd..e409400675962e2649563c84f803d1f67da18a96 100644 (file)
@@ -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
 
 /**
@@ -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
 > {
   /**
@@ -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.
@@ -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
@@ -371,7 +371,7 @@ 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>) &
@@ -379,21 +379,22 @@ export type Store<
   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.
index 98e4bb19de9f65ac891ce74fd218ce4e5ece6c1c..7d99bb47320c1714cb8919592106d1de5a35d9c2 100644 (file)
@@ -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<GenericStore>(store)
+  expectType<Store>(store)
   expectType<Pinia>(pinia)
   expectType<App>(app)
   expectType<
index 47bc00ee8284166846c30f9894f7c94b9a39787a..62851f589ceae5b00d88e62e66e88291466f1804 100644 (file)
@@ -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<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')