]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
fix(types): forbid non existant access in getters and actions
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 24 Jun 2021 10:04:47 +0000 (12:04 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Thu, 24 Jun 2021 13:16:23 +0000 (15:16 +0200)
src/store.ts
src/types.ts
test-dts/store.test-d.ts

index 5e44cffcc9a79d31dd4b148e8dfea3ede8d0c351..71f3b4f88a2e6999f5132b2e5189864abc5c90ce 100644 (file)
@@ -377,12 +377,10 @@ export function defineStore<
     if (pinia) setActivePinia(pinia)
 
     pinia = getActivePinia()
+    let storeCache = storesMap.get(pinia)
+    if (!storeCache) storesMap.set(pinia, (storeCache = new Map()))
 
-    let stores = storesMap.get(pinia)
-    if (!stores) storesMap.set(pinia, (stores = new Map()))
-
-    // let store = stores.get(id) as Store<Id, S, G, A>
-    let storeAndDescriptor = stores.get(id) as
+    let storeAndDescriptor = storeCache.get(id) as
       | [
           StoreWithState<Id, S, G, A>,
           StateDescriptor<S>,
@@ -394,7 +392,7 @@ export function defineStore<
       storeAndDescriptor = initStore(id, state, pinia.state.value[id])
 
       // @ts-expect-error: annoying to type
-      stores.set(id, storeAndDescriptor)
+      storeCache.set(id, storeAndDescriptor)
 
       if (__DEV__ && isClient) {
         // @ts-expect-error: annoying to type
index 9ad83e3a28eb458cc8a9024b960e26510017d966..42e15805e0b0214360ae654f5798f105e6f0f7b7 100644 (file)
@@ -216,7 +216,8 @@ export interface StoreWithState<
   /**
    * State of the Store. Setting it will replace the whole state.
    */
-  $state: UnwrapRef<S> & PiniaCustomStateProperties<S>
+  $state: UnwrapRef<StateTree extends S ? {} : S> &
+    PiniaCustomStateProperties<S>
 
   /**
    * Private property defining the pinia the store is attached to.
@@ -368,7 +369,7 @@ export type Store<
  */
 export type GenericStore<
   Id extends string = string,
-  S extends StateTree = StateTree,
+  S extends StateTree = any,
   G extends GettersTree<S> = GettersTree<S>,
   // has the actions without the context (this) for typings
   A /* extends ActionsTree */ = ActionsTree
@@ -423,7 +424,12 @@ export interface PiniaCustomStateProperties<S extends StateTree = StateTree> {}
  */
 export type GettersTree<S extends StateTree> = Record<
   string,
-  ((state: UnwrapRef<S & PiniaCustomStateProperties<S>>) => any) | (() => any)
+  | ((
+      state: UnwrapRef<
+        (StateTree extends S ? {} : S) & PiniaCustomStateProperties<S>
+      >
+    ) => any)
+  | (() => any)
 >
 
 /**
@@ -445,16 +451,18 @@ export interface DefineStoreOptions<
    * Unique string key to identify the store across the application.
    */
   id: Id
+
   /**
    * Function to create a fresh state.
    */
   state?: () => S
+
   /**
    * Optional object of getters.
    */
   getters?: G &
     ThisType<
-      UnwrapRef<S> &
+      UnwrapRef<StateTree extends S ? {} : S> &
         StoreWithGetters<G> &
         PiniaCustomProperties &
         PiniaCustomStateProperties
@@ -465,9 +473,9 @@ export interface DefineStoreOptions<
   actions?: A &
     ThisType<
       A &
-        UnwrapRef<S> &
+        UnwrapRef<StateTree extends S ? {} : S> &
         StoreWithState<Id, S, G, A> &
-        StoreWithGetters<G> &
+        StoreWithGetters<GettersTree<S> extends G ? {} : G> &
         PiniaCustomProperties &
         PiniaCustomStateProperties
     >
index 70ab1938d1b9382f9fc832c94b0af9733f439e2f..32402b06fa1ce6f538d13d3c5d6760fe0d5ded5a 100644 (file)
@@ -25,6 +25,8 @@ const useStore = defineStore({
   },
   actions: {
     doStuff() {
+      // @ts-expect-error
+      this.notExisting
       expectType<string>(this.upper)
       expectType<false>(this.other)
     },
@@ -34,6 +36,73 @@ const useStore = defineStore({
   },
 })
 
+// actions on not existing properties
+defineStore({
+  id: '',
+  actions: {
+    a() {
+      // @ts-expect-error
+      this.notExisting
+    },
+  },
+})
+
+defineStore({
+  id: '',
+  state: () => ({}),
+  actions: {
+    a() {
+      // @ts-expect-error
+      this.notExisting
+    },
+  },
+})
+
+defineStore({
+  id: '',
+  getters: {},
+  actions: {
+    a() {
+      // @ts-expect-error
+      this.notExisting
+    },
+  },
+})
+
+// getters on not existing properties
+defineStore({
+  id: '',
+  getters: {
+    a(): number {
+      // @ts-expect-error
+      this.notExisting
+      return 2
+    },
+    b: (state) => {
+      // @ts-expect-error
+      state.notExisting
+      return
+    },
+  },
+})
+
+defineStore({
+  id: '',
+  state: () => ({}),
+  getters: {
+    a(): number {
+      // @ts-expect-error
+      this.notExisting
+      return 2
+    },
+    b: (state) => {
+      // @ts-expect-error
+      state.notExisting
+      return
+    },
+  },
+})
+
 const store = useStore()
 
 expectType<{ a: 'on' | 'off' }>(store.$state)
@@ -169,6 +238,5 @@ 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')
+useSyncValueToStore(() => 2, genericStore, 'random')