]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
fix(types): forbid non existants keys on store
authorEduardo San Martin Morote <posva13@gmail.com>
Sat, 15 May 2021 18:22:55 +0000 (20:22 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Sat, 15 May 2021 20:01:27 +0000 (22:01 +0200)
src/devtools/formatting.ts
src/index.ts
src/mapHelpers.ts
src/store.ts
src/types.ts
test-dts/customizations.test-d.ts
test-dts/mapHelpers.test-d.ts
test-dts/plugins.test-d.ts
test-dts/store.test-d.ts

index 54bba4766b0a018b25165363e72ec5adf56f50d4..d81966755aed743ba2a939cd8e99628e2d539096 100644 (file)
@@ -32,6 +32,7 @@ export function formatStoreForInspectorState(
       editable: false,
       key: 'getters',
       value: store._getters.reduce((getters, key) => {
+        // @ts-expect-error
         getters[key] = store[key]
         return getters
       }, {} as GettersTree<StateTree>),
index 7c577382a043cb56b030b5ac841ad66ff7d46d64..8f1e5702b84b214c9954412f403ea4b075737163 100644 (file)
@@ -11,6 +11,7 @@ export type {
   StoreDefinition,
   StoreWithGetters,
   GettersTree,
+  ActionsTree,
   _Method,
   StoreWithActions,
   StoreWithState,
index 419f4a9af63506410732f004ccff0541f7a9ce74..00b8c9004d098cd7e75a1f5407fcf4e4ead4139d 100644 (file)
@@ -139,7 +139,14 @@ export function mapStores<Stores extends any[]>(
  * @internal
  */
 export type _MapStateReturn<S extends StateTree, G extends GettersTree<S>> = {
-  [key in keyof S | keyof G]: () => Store<string, S, G, {}>[key]
+  // [key in keyof S | keyof G]: () => key extends keyof S
+  //   ? S[key]
+  //   : key extends keyof G
+  //   ? G[key]
+  //   : never
+  [key in keyof S | keyof G]: () => key extends keyof Store<string, S, G, {}>
+    ? Store<string, S, G, {}>[key]
+    : never
 }
 
 /**
@@ -157,7 +164,7 @@ export type _MapStateObjectReturn<
 > = {
   [key in keyof T]: () => T[key] extends (store: Store) => infer R
     ? R
-    : T[key] extends keyof S | keyof G
+    : T[key] extends keyof Store<Id, S, G, A>
     ? Store<Id, S, G, A>[T[key]]
     : never
 }
@@ -267,6 +274,7 @@ export function mapState<
   return Array.isArray(keysOrMapper)
     ? keysOrMapper.reduce((reduced, key) => {
         reduced[key] = function (this: ComponentPublicInstance) {
+          // @ts-expect-error
           return getCachedStore(this, useStore)[key]
         } as () => any
         return reduced
@@ -279,7 +287,7 @@ export function mapState<
           // function
           return typeof storeKey === 'function'
             ? (storeKey as (store: Store<Id, S, G, A>) => any).call(this, store)
-            : store[storeKey as keyof S | keyof G]
+            : store[storeKey as keyof typeof store]
         }
         return reduced
       }, {} as _MapStateObjectReturn<Id, S, G, A, KeyMapper>)
@@ -295,14 +303,14 @@ export const mapGetters = mapState
  * @internal
  */
 export type _MapActionsReturn<A> = {
-  [key in keyof A]: Store<string, StateTree, {}, A>[key]
+  [key in keyof A]: A[key]
 }
 
 /**
  * @internal
  */
 export type _MapActionsObjectReturn<A, T extends Record<string, keyof A>> = {
-  [key in keyof T]: Store<string, StateTree, {}, A>[T[key]]
+  [key in keyof T]: A[T[key]]
 }
 
 /**
@@ -392,21 +400,25 @@ export function mapActions<
 ): _MapActionsReturn<A> | _MapActionsObjectReturn<A, KeyMapper> {
   return Array.isArray(keysOrMapper)
     ? keysOrMapper.reduce((reduced, key) => {
+        // @ts-expect-error
         reduced[key] = function (
           this: ComponentPublicInstance,
           ...args: any[]
         ) {
+          // @ts-expect-error
           return (getCachedStore(this, useStore)[key] as _Method)(...args)
-        } as Store<string, StateTree, {}, A>[keyof A]
+        }
         return reduced
       }, {} as _MapActionsReturn<A>)
     : Object.keys(keysOrMapper).reduce((reduced, key: keyof KeyMapper) => {
+        // @ts-expect-error
         reduced[key] = function (
           this: ComponentPublicInstance,
           ...args: any[]
         ) {
+          // @ts-expect-error
           return getCachedStore(this, useStore)[keysOrMapper[key]](...args)
-        } as Store<string, StateTree, {}, A>[keyof KeyMapper[]]
+        }
         return reduced
       }, {} as _MapActionsObjectReturn<A, KeyMapper>)
 }
@@ -416,8 +428,8 @@ export function mapActions<
  */
 export type _MapWritableStateReturn<S extends StateTree> = {
   [key in keyof S]: {
-    get: () => Store<string, S, {}, {}>[key]
-    set: (value: Store<string, S, {}, {}>[key]) => any
+    get: () => S[key]
+    set: (value: S[key]) => any
   }
 }
 
@@ -429,8 +441,8 @@ export type _MapWritableStateObjectReturn<
   T extends Record<string, keyof S>
 > = {
   [key in keyof T]: {
-    get: () => Store<string, S, {}, {}>[T[key]]
-    set: (value: Store<string, S, {}, {}>[T[key]]) => any
+    get: () => S[T[key]]
+    set: (value: S[T[key]]) => any
   }
 }
 
@@ -492,10 +504,12 @@ export function mapWritableState<
         // @ts-ignore
         reduced[key] = {
           get(this: ComponentPublicInstance) {
+            // @ts-expect-error
             return getCachedStore(this, useStore)[key]
           },
           set(this: ComponentPublicInstance, value) {
             // it's easier to type it here as any
+            // @ts-expect-error
             return (getCachedStore(this, useStore)[key] = value as any)
           },
         }
@@ -505,10 +519,12 @@ export function mapWritableState<
         // @ts-ignore
         reduced[key] = {
           get(this: ComponentPublicInstance) {
+            // @ts-expect-error
             return getCachedStore(this, useStore)[keysOrMapper[key]]
           },
           set(this: ComponentPublicInstance, value) {
             // it's easier to type it here as any
+            // @ts-expect-error
             return (getCachedStore(this, useStore)[keysOrMapper[key]] =
               value as any)
           },
index 1f459b89e0909e0c02c636d8ff2c52acda85ba95..3570b1bcf3602b8473257a7b699cc3dc0611f3c9 100644 (file)
@@ -425,8 +425,8 @@ export function defineStore<
         storeAndDescriptor[0],
         storeAndDescriptor[1],
         id,
-        getters as GettersTree<S> | undefined,
-        actions as A | undefined,
+        getters,
+        actions,
         options
       )
 
@@ -437,20 +437,19 @@ export function defineStore<
       }
     } else {
       store =
-        // null avoids the warning for not found injection key
         (currentInstance && inject(storeAndDescriptor[2], null)) ||
         buildStoreToUse<
           Id,
           S,
           G,
-          // @ts-expect-error: A without extends
+          // @ts-expect-error: cannot extends ActionsTree
           A
         >(
           storeAndDescriptor[0],
           storeAndDescriptor[1],
           id,
-          getters as GettersTree<S> | undefined,
-          actions as A | undefined,
+          getters,
+          actions,
           options
         )
     }
index b83fa2c348fe3f8620df5060e967904621725584..4fec98dc03d7dec07ca801d28f8ae7795bc17187 100644 (file)
@@ -231,7 +231,8 @@ export interface StoreWithState<
   /**
    * State of the Store. Setting it will replace the whole state.
    */
-  $state: UnwrapRef<S> & PiniaCustomStateProperties<S>
+  $state: (StateTree extends S ? {} : UnwrapRef<S>) &
+    PiniaCustomStateProperties<S>
 
   /**
    * Private property defining the pinia the store is attached to.
@@ -377,12 +378,29 @@ export type Store<
   // has the actions without the context (this) for typings
   A /* extends ActionsTree */ = ActionsTree
 > = StoreWithState<Id, S, G, A> &
-  UnwrapRef<S> &
-  StoreWithGetters<G> &
-  StoreWithActions<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
+ */
+export type GenericStore = StoreWithState<
+  string,
+  StateTree,
+  GettersTree<StateTree>,
+  ActionsTree
+> &
+  PiniaCustomProperties<
+    string,
+    StateTree,
+    GettersTree<StateTree>,
+    ActionsTree
+  > &
+  PiniaCustomStateProperties<StateTree>
+
 /**
  * Return type of `defineStore()`. Function that allows instantiating a store.
  */
@@ -405,12 +423,6 @@ export interface StoreDefinition<
   $id: Id
 }
 
-/**
- * Generic version of Store.
- * @deprecated Use Store instead
- */
-export type GenericStore = Store
-
 /**
  * Properties that are added to every store by `pinia.use()`
  */
index e37c4972a148537fcdb9a53c6bafec4d179b33b5..0d4407da4d15bd96aba007fdbc291ccb16c0bb38 100644 (file)
@@ -1,6 +1,5 @@
-import { mapStores } from 'dist/pinia'
+import { expectType, createPinia, defineStore, mapStores } from './'
 import { App } from 'vue'
-import { expectType, createPinia, defineStore } from '.'
 
 declare module '../dist/pinia' {
   export interface MapStoresCustomization {
@@ -40,7 +39,6 @@ pinia.use((context) => {
 
 const useStore = defineStore({
   id: 'main',
-  state: () => ({}),
   actions: {
     one() {},
     two() {
@@ -74,6 +72,7 @@ pinia.use(({ options, store }) => {
   if (options.debounce) {
     return Object.keys(options.debounce).reduce((debouncedActions, action) => {
       debouncedActions[action] = debounce(
+        // @ts-expect-error: cannot be inferred
         store[action],
         options.debounce![action as keyof typeof options['actions']]
       )
index 72dbd8039798c4c5fd9661722668bd740a518743..9fc766096c24699208e7065d5202975befdd3992 100644 (file)
@@ -5,7 +5,7 @@ import {
   mapActions,
   mapState,
   mapWritableState,
-} from '.'
+} from './'
 
 const useStore = defineStore({
   id: 'name',
index efb6d226b6e3b639d9290660e58f3e0fb4d77ec4..98e4bb19de9f65ac891ce74fd218ce4e5ece6c1c 100644 (file)
@@ -6,7 +6,7 @@ import {
   Pinia,
   StateTree,
   DefineStoreOptions,
-} from '.'
+} from './'
 
 const pinia = createPinia()
 
index e31749846cefb6bcd011d509ce45fda634e6c4fd..47bc00ee8284166846c30f9894f7c94b9a39787a 100644 (file)
@@ -33,7 +33,7 @@ const useStore = defineStore({
   },
 })
 
-let store = useStore()
+const store = useStore()
 
 expectType<{ a: 'on' | 'off' }>(store.$state)
 expectType<number>(store.nested.counter)
@@ -56,3 +56,65 @@ store.$patch(() => {
   // return earlier
   return
 })
+
+const useNoSAG = defineStore({
+  id: 'noSAG',
+})
+const useNoAG = defineStore({
+  id: 'noAG',
+  state: () => ({}),
+})
+const useNoSG = defineStore({
+  id: 'noAG',
+  actions: {},
+})
+const useNoSA = defineStore({
+  id: 'noAG',
+  getters: {},
+})
+const useNoS = defineStore({
+  id: 'noAG',
+  actions: {},
+  getters: {},
+})
+const useNoA = defineStore({
+  id: 'noAG',
+  state: () => ({}),
+  getters: {},
+})
+const useNoG = defineStore({
+  id: 'noAG',
+  state: () => ({}),
+  actions: {},
+})
+
+const noSAG = useNoSAG()
+const noSA = useNoSA()
+const noAG = useNoAG()
+const noSG = useNoSG()
+const noS = useNoS()
+const noA = useNoA()
+const noG = useNoG()
+
+// @ts-expect-error
+store.notExisting
+
+// @ts-expect-error
+noSAG.notExisting
+// @ts-expect-error
+noSAG.$state.hey
+
+// @ts-expect-error
+noSA.notExisting
+// @ts-expect-error
+noSA.notExisting
+// @ts-expect-error
+noAG.notExisting
+// @ts-expect-error
+noSG.notExisting
+// @ts-expect-error
+noS.notExisting
+// @ts-expect-error
+noA.notExisting
+// @ts-expect-error
+noG.notExisting