From: Eduardo San Martin Morote Date: Fri, 23 Jul 2021 11:14:40 +0000 (+0200) Subject: fix(types): actual generic store X-Git-Tag: v2.0.0-rc.0~24 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e4c541fdd17ea97e25dfd45bd3378732ff6a344d;p=thirdparty%2Fvuejs%2Fpinia.git fix(types): actual generic store BREAKING CHANGE: The existing `Store` types was trying to be generic when no types were specified but failing at it. Now, `Store` without any type will default to an empty Store. This enables a stricter version of `defineStore` when any of state, getters, and actions are missing. If you were using `Store` as a type, you should now use `StoreGeneric` instead, which also replaces `GenericStore` (marked as deprecated). ```diff -function takeAnyStore(store: Store) {} +function takeAnyStore(store: StoreGeneric) {} ``` --- diff --git a/src/createPinia.ts b/src/createPinia.ts index fb8ce0d6..5f4bf497 100644 --- a/src/createPinia.ts +++ b/src/createPinia.ts @@ -7,7 +7,7 @@ import { import { ref, App, markRaw, effectScope } from 'vue' import { registerPiniaDevtools, devtoolsPlugin } from './devtools' import { IS_CLIENT } from './env' -import { StateTree, Store } from './types' +import { StateTree, StoreGeneric } from './types' /** * Creates a Pinia instance to be used by the application @@ -52,7 +52,7 @@ export function createPinia(): Pinia { // @ts-expect-error _a: null, _e: scope, - _s: new Map(), + _s: new Map(), state, }) diff --git a/src/devtools/formatting.ts b/src/devtools/formatting.ts index 892bbe24..212c79d8 100644 --- a/src/devtools/formatting.ts +++ b/src/devtools/formatting.ts @@ -3,7 +3,7 @@ import { CustomInspectorNode, CustomInspectorState, } from '@vue/devtools-api' -import { Store, MutationType } from '../types' +import { MutationType, StoreGeneric } from '../types' import { DebuggerEvent } from 'vue' import { Pinia } from '../rootStore' import { isPinia } from './utils' @@ -20,7 +20,7 @@ export const PINIA_ROOT_LABEL = '🍍 Pinia (root)' export const PINIA_ROOT_ID = '_root' export function formatStoreForInspectorTree( - store: Store | Pinia + store: StoreGeneric | Pinia ): CustomInspectorNode { return '$id' in store ? { @@ -34,7 +34,7 @@ export function formatStoreForInspectorTree( } export function formatStoreForInspectorState( - store: Store | Pinia + store: StoreGeneric | Pinia ): CustomInspectorState { if (isPinia(store)) { const state: CustomInspectorState = { @@ -63,7 +63,6 @@ export function formatStoreForInspectorState( state: Object.keys(store.$state).map((key) => ({ editable: true, key, - // @ts-expect-error value: store.$state[key], })), } @@ -73,7 +72,6 @@ export function formatStoreForInspectorState( state.getters = store._getters.map((getterName) => ({ editable: false, key: getterName, - // @ts-expect-error value: store[getterName], })) } @@ -82,7 +80,6 @@ export function formatStoreForInspectorState( state.customProperties = Array.from(store._customProperties).map((key) => ({ editable: true, key, - // @ts-expect-error value: store[key], })) } diff --git a/src/devtools/plugin.ts b/src/devtools/plugin.ts index d8d59bc7..36333d36 100644 --- a/src/devtools/plugin.ts +++ b/src/devtools/plugin.ts @@ -2,11 +2,11 @@ import { setupDevtoolsPlugin, TimelineEvent } from '@vue/devtools-api' import { App, ComponentPublicInstance, toRaw } from 'vue' import { Pinia, PiniaPluginContext } from '../rootStore' import { - Store, GettersTree, MutationType, StateTree, ActionsTree, + StoreGeneric, } from '../types' import { actionGlobalCopyState, @@ -130,7 +130,6 @@ export function registerPiniaDevtools(app: App, pinia: Pinia) { key: 'getters', editable: false, value: store._getters.reduce((getters, key) => { - // @ts-expect-error getters[key] = store[key] return getters }, {} as GettersTree), @@ -142,7 +141,7 @@ export function registerPiniaDevtools(app: App, pinia: Pinia) { api.on.getInspectorTree((payload) => { if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { - let stores: Array = [pinia] + let stores: Array = [pinia] stores = stores.concat(Array.from(pinia._s.values())) payload.rootNodes = ( @@ -239,7 +238,7 @@ export function registerPiniaDevtools(app: App, pinia: Pinia) { ) } -function addStoreToDevtools(app: App, store: Store) { +function addStoreToDevtools(app: App, store: StoreGeneric) { if (!componentStateTypes.includes(getStoreType(store.$id))) { componentStateTypes.push(getStoreType(store.$id)) } @@ -370,17 +369,15 @@ let activeAction: number | undefined * @param store - store to patch * @param actionNames - list of actionst to patch */ -function patchActionForGrouping(store: Store, actionNames: string[]) { +function patchActionForGrouping(store: StoreGeneric, actionNames: string[]) { // original actions of the store as they are given by pinia. We are going to override them const actions = actionNames.reduce((storeActions, actionName) => { // use toRaw to avoid tracking #541 - // @ts-expect-error storeActions[actionName] = toRaw(store)[actionName] return storeActions }, {} as ActionsTree) for (const actionName in actions) { - // @ts-expect-error store[actionName] = function () { // setActivePinia(store._p) // the running action id is incremented in a before action hook @@ -409,7 +406,7 @@ function patchActionForGrouping(store: Store, actionNames: string[]) { export function devtoolsPlugin< Id extends string = string, S extends StateTree = StateTree, - G extends GettersTree = GettersTree, + G /* extends GettersTree */ = GettersTree, A /* extends ActionsTree */ = ActionsTree >({ app, store, options }: PiniaPluginContext) { // HMR module diff --git a/src/hmr.ts b/src/hmr.ts index ab2e76d1..61ad82e9 100644 --- a/src/hmr.ts +++ b/src/hmr.ts @@ -1,6 +1,6 @@ import { isRef, isReactive } from 'vue' import { Pinia } from './rootStore' -import { isPlainObject, Store, StoreDefinition, _Method } from './types' +import { isPlainObject, StoreDefinition, StoreGeneric, _Method } from './types' /** * Checks if a function is a `StoreDefinition` @@ -66,10 +66,7 @@ export function patchObject( * @param initialUseStore - return of the defineStore to hot update * @param hot - `import.meta.hot` */ -export function acceptHMRUpdate( - initialUseStore: StoreDefinition, - hot: any -) { +export function acceptHMRUpdate(initialUseStore: StoreDefinition, hot: any) { return (newModule: any) => { const pinia: Pinia | undefined = hot.data.pinia || initialUseStore._pinia @@ -97,7 +94,7 @@ export function acceptHMRUpdate( return hot.invalidate() } - const existingStore: Store = pinia._s.get(id)! + const existingStore: StoreGeneric = pinia._s.get(id)! if (!existingStore) { console.log(`skipping hmr because store doesn't exist yet`) return diff --git a/src/index.ts b/src/index.ts index bf7b29fd..7b801247 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ export { defineStore } from './store' export type { StateTree, Store, + StoreGeneric, GenericStore, StoreDefinition, StoreWithGetters, diff --git a/src/mapHelpers.ts b/src/mapHelpers.ts index 7d4a5eba..43f24680 100644 --- a/src/mapHelpers.ts +++ b/src/mapHelpers.ts @@ -146,7 +146,7 @@ export type _MapStateObjectReturn< keyof S | keyof G | ((store: Store) => any) > > = { - [key in keyof T]: () => T[key] extends (store: Store) => infer R + [key in keyof T]: () => T[key] extends (store: any) => infer R ? R : T[key] extends keyof Store ? Store[T[key]] @@ -260,7 +260,6 @@ export function mapState< return Array.isArray(keysOrMapper) ? keysOrMapper.reduce((reduced, key) => { reduced[key] = function (this: ComponentPublicInstance) { - // @ts-expect-error return useStore(this.$pinia)[key] } as () => any return reduced @@ -392,8 +391,7 @@ export function mapActions< this: ComponentPublicInstance, ...args: any[] ) { - // @ts-expect-error - return (useStore(this.$pinia)[key] as _Method)(...args) + return useStore(this.$pinia)[key](...args) } return reduced }, {} as _MapActionsReturn) @@ -403,7 +401,6 @@ export function mapActions< this: ComponentPublicInstance, ...args: any[] ) { - // @ts-expect-error return useStore(this.$pinia)[keysOrMapper[key]](...args) } return reduced @@ -491,12 +488,10 @@ export function mapWritableState< // @ts-ignore reduced[key] = { get(this: ComponentPublicInstance) { - // @ts-expect-error return useStore(this.$pinia)[key] }, set(this: ComponentPublicInstance, value) { // it's easier to type it here as any - // @ts-expect-error return (useStore(this.$pinia)[key] = value as any) }, } @@ -506,12 +501,10 @@ export function mapWritableState< // @ts-ignore reduced[key] = { get(this: ComponentPublicInstance) { - // @ts-expect-error return useStore(this.$pinia)[keysOrMapper[key]] }, set(this: ComponentPublicInstance, value) { // it's easier to type it here as any - // @ts-expect-error return (useStore(this.$pinia)[keysOrMapper[key]] = value as any) }, } diff --git a/src/rootStore.ts b/src/rootStore.ts index 955dcda0..9b3eaa5a 100644 --- a/src/rootStore.ts +++ b/src/rootStore.ts @@ -1,16 +1,14 @@ import { App, EffectScope, InjectionKey, Plugin, Ref, warn } from 'vue' import { StateTree, - StoreWithState, - StateDescriptor, PiniaCustomProperties, _Method, Store, GettersTree, ActionsTree, PiniaCustomStateProperties, - GenericStore, DefineStoreOptionsInPlugin, + StoreGeneric, } from './types' /** @@ -44,23 +42,6 @@ export const getActivePinia = () => { return activePinia! } -/** - * Map of stores based on a Pinia instance. Allows setting and retrieving stores - * for the current running application (with its pinia). - */ - -export const storesMap = new WeakMap< - Pinia, - Map< - string, - [ - StoreWithState, - StateDescriptor, - InjectionKey - ] - > ->() - /** * Every application must own its own pinia to be able to create stores */ @@ -105,7 +86,7 @@ export interface Pinia { * * @internal */ - _s: Map + _s: Map /** * Added by `createTestingPinia()` to bypass `useStore(pinia)`. @@ -128,7 +109,7 @@ declare module '@vue/runtime-core' { * * @internal */ - _pStores?: Record + _pStores?: Record } } @@ -142,7 +123,7 @@ export const piniaSymbol = ( export interface PiniaPluginContext< Id extends string = string, S extends StateTree = StateTree, - G extends GettersTree = GettersTree, + G /* extends GettersTree */ = GettersTree, A /* extends ActionsTree */ = ActionsTree > { /** diff --git a/src/store.ts b/src/store.ts index 5e6e219b..14d324d7 100644 --- a/src/store.ts +++ b/src/store.ts @@ -35,6 +35,7 @@ import { _UnionToTuple, DefineSetupStoreOptions, DefineStoreOptionsInPlugin, + StoreGeneric, } from './types' import { getActivePinia, @@ -435,9 +436,7 @@ function createSetupStore< store._hotUpdate = markRaw((newStore) => { newStore._hmrPayload.state.forEach((stateKey) => { if (stateKey in store.$state) { - // @ts-expect-error const newStateTarget = newStore.$state[stateKey] - // @ts-expect-error const oldStateSource = store.$state[stateKey] if ( typeof newStateTarget === 'object' && @@ -446,7 +445,6 @@ function createSetupStore< ) { patchObject(newStateTarget, oldStateSource) } else { - // @ts-expect-error // transfer the ref newStore.$state[stateKey] = oldStateSource } @@ -460,7 +458,6 @@ function createSetupStore< // remove deleted state properties Object.keys(store.$state).forEach((stateKey) => { if (!(stateKey in newStore.$state)) { - // @ts-expect-error delete store[stateKey] } }) @@ -468,9 +465,7 @@ function createSetupStore< pinia.state.value[$id] = toRef(newStore._hmrPayload, 'hotState') for (const actionName in newStore._hmrPayload.actions) { - const action: _Method = - // @ts-expect-error - newStore[actionName] + const action: _Method = newStore[actionName] // @ts-expect-error: new key store[actionName] = @@ -496,7 +491,6 @@ function createSetupStore< // remove deleted getters Object.keys(store._hmrPayload.getters).forEach((key) => { if (!(key in newStore._hmrPayload.getters)) { - // @ts-expect-error delete store[key] } }) @@ -504,7 +498,6 @@ function createSetupStore< // remove old actions Object.keys(store._hmrPayload.actions).forEach((key) => { if (!(key in newStore._hmrPayload.actions)) { - // @ts-expect-error delete store[key] } }) @@ -572,7 +565,7 @@ function createSetupStore< return store } -// export function disposeStore(store: Store) { +// export function disposeStore(store: StoreGeneric) { // store._e // } @@ -635,10 +628,10 @@ type _ExtractGettersFromSetupStore = _SpreadPropertiesFromObject< */ export function defineStore< Id extends string, - S extends StateTree, - G extends GettersTree, + S extends StateTree = {}, + G extends GettersTree = {}, // cannot extends ActionsTree because we loose the typings - A /* extends ActionsTree */ + A /* extends ActionsTree */ = {} >( id: Id, options: Omit, 'id'> @@ -651,10 +644,10 @@ export function defineStore< */ export function defineStore< Id extends string, - S extends StateTree, - G extends GettersTree, + S extends StateTree = {}, + G extends GettersTree = {}, // cannot extends ActionsTree because we loose the typings - A /* extends ActionsTree */ + A /* extends ActionsTree */ = {} >(options: DefineStoreOptions): StoreDefinition /** @@ -679,7 +672,12 @@ export function defineStore( _ExtractGettersFromSetupStore, _ExtractActionsFromSetupStore > -export function defineStore(idOrOptions: any, setup?: any, setupOptions?: any) { +export function defineStore( + // TODO: add proper types from above + idOrOptions: any, + setup?: any, + setupOptions?: any +): StoreDefinition { let id: string let options: | DefineStoreOptions, ActionsTree> @@ -699,7 +697,7 @@ export function defineStore(idOrOptions: any, setup?: any, setupOptions?: any) { id = idOrOptions.id } - function useStore(pinia?: Pinia | null, hot?: Store): Store { + function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric { const currentInstance = getCurrentInstance() pinia = // in test mode, ignore the argument provided as we can always retrieve a @@ -724,7 +722,7 @@ export function defineStore(idOrOptions: any, setup?: any, setupOptions?: any) { } } - const store: Store = pinia._s.get(id)! + const store: StoreGeneric = pinia._s.get(id)! if (__DEV__ && hot) { const hotId = '__hot:' + id @@ -736,7 +734,7 @@ export function defineStore(idOrOptions: any, setup?: any, setupOptions?: any) { true ) - hot._hotUpdate(newStore as any) + hot._hotUpdate(newStore) // cleanup the state properties and the store from the cache delete pinia.state.value[hotId] @@ -757,7 +755,8 @@ export function defineStore(idOrOptions: any, setup?: any, setupOptions?: any) { cache[id] = store } - return store + // StoreGeneric cannot be casted towards Store + return store as any } useStore.$id = id diff --git a/src/testing.ts b/src/testing.ts index 75db70d2..d08b844f 100644 --- a/src/testing.ts +++ b/src/testing.ts @@ -93,11 +93,7 @@ export function createTestingPinia({ Object.keys(options.actions || {}).forEach((action) => { actionsCache[action] = actionsCache[action] || - (stubActions - ? createSpy!() - : // @ts-expect-error: - createSpy!(store[action])) - // @ts-expect-error: + (stubActions ? createSpy!() : createSpy!(store[action])) store[action] = actionsCache[action] }) diff --git a/src/types.ts b/src/types.ts index 63b5a876..7367e5e4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,7 +9,7 @@ export type StateTree = Record /** * Object descriptor for Object.defineProperty */ -export interface StateDescriptor { +export interface StateDescriptor { get(): S set(newValue: S): void } @@ -163,7 +163,7 @@ export type SubscriptionCallback = ( export type StoreOnActionListenerContext< Id extends string, S extends StateTree, - G extends GettersTree, + G /* extends GettersTree */, A /* extends ActionsTree */ > = { [Name in keyof A]: { @@ -175,7 +175,7 @@ export type StoreOnActionListenerContext< /** * Store that is invoking the action */ - store: Store + store: ActionsTree extends A ? StoreGeneric : Store /** * Parameters passed to the action @@ -208,11 +208,19 @@ export type StoreOnActionListenerContext< * Argument of `store.$onAction()` */ export type StoreOnActionListener< - Id extends string = string, - S extends StateTree = StateTree, - G extends GettersTree = GettersTree, - A /* extends ActionsTree */ = ActionsTree -> = (context: StoreOnActionListenerContext) => void + Id extends string, + S extends StateTree, + G /* extends GettersTree */, + A /* extends ActionsTree */ +> = ( + context: StoreOnActionListenerContext< + Id, + S, + G, + // {} creates a type of never due to how StoreOnActionListenerContext is defined + {} extends A ? ActionsTree : A + > +) => void /** * Base store with state and functions @@ -221,8 +229,8 @@ export type StoreOnActionListener< export interface StoreWithState< Id extends string, S extends StateTree, - G extends GettersTree = GettersTree, - A /* extends ActionsTree */ = ActionsTree + G /* extends GettersTree */, + A /* extends ActionsTree */ > { /** * Unique identifier of the store @@ -232,8 +240,7 @@ export interface StoreWithState< /** * State of the Store. Setting it will replace the whole state. */ - $state: UnwrapRef & - PiniaCustomStateProperties + $state: UnwrapRef & PiniaCustomStateProperties /** * Private property defining the pinia the store is attached to. @@ -250,7 +257,9 @@ export interface StoreWithState< _getters?: string[] /** - * Used by devtools plugin to retrieve properties added with plugins. Removed in production. + * Used by devtools plugin to retrieve properties added with plugins. Removed + * in production. Can be used by the user to add property keys of the store + * that should be displayed in devtools. * * @internal */ @@ -261,7 +270,7 @@ export interface StoreWithState< * * @internal */ - _hotUpdate(useStore: Store): void + _hotUpdate(useStore: StoreGeneric): void /** * Payload of the hmr update. Dev only. @@ -393,26 +402,39 @@ export type StoreWithGetters = { */ export type Store< Id extends string = string, - S extends StateTree = StateTree, - G extends GettersTree = GettersTree, + S extends StateTree = {}, + G /* extends GettersTree*/ = {}, // has the actions without the context (this) for typings - A /* extends ActionsTree */ = ActionsTree -> = StoreWithState & - (StateTree extends S ? {} : UnwrapRef) & - (GettersTree extends G ? {} : StoreWithGetters) & + A /* extends ActionsTree */ = {} +> = StoreWithState & + UnwrapRef & + StoreWithGetters & + // StoreWithActions & (ActionsTree extends A ? {} : StoreWithActions) & PiniaCustomProperties & PiniaCustomStateProperties +/** + * 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 StoreGeneric = Store< + string, + StateTree, + GettersTree, + ActionsTree +> /** * 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. + * @deprecated Use `StoreGeneric` instead */ export type GenericStore< Id extends string = string, - S extends StateTree = any, - G extends GettersTree = GettersTree, + S extends StateTree = StateTree, + G /* extends GettersTree */ = GettersTree, // has the actions without the context (this) for typings A /* extends ActionsTree */ = ActionsTree > = StoreWithState & @@ -428,7 +450,7 @@ export type GenericStore< export interface StoreDefinition< Id extends string = string, S extends StateTree = StateTree, - G extends GettersTree = GettersTree, + G /* extends GettersTree*/ = GettersTree, A /* extends ActionsTree */ = ActionsTree > { /** @@ -437,7 +459,12 @@ export interface StoreDefinition< * @param pinia - Pinia instance to retrieve the store * @param hot - dev only hot module replacement */ - (pinia?: Pinia | null | undefined, hot?: Store): Store + (pinia?: Pinia | null | undefined, hot?: Store): Store< + Id, + S, + G, + A + > /** * Id of the store. Used by map helpers. @@ -458,7 +485,7 @@ export interface StoreDefinition< export interface PiniaCustomProperties< Id extends string = string, S extends StateTree = StateTree, - G extends GettersTree = GettersTree, + G /* extends GettersTree */ = GettersTree, A /* extends ActionsTree */ = ActionsTree > {} @@ -474,11 +501,7 @@ export interface PiniaCustomStateProperties {} */ export type GettersTree = Record< string, - | (( - state: UnwrapRef< - (StateTree extends S ? {} : S) & PiniaCustomStateProperties - > - ) => any) + | ((state: UnwrapRef & UnwrapRef>) => any) | (() => any) > @@ -496,7 +519,7 @@ export type ActionsTree = Record export interface DefineStoreOptions< Id extends string, S extends StateTree, - G extends GettersTree, + G /* extends GettersTree */, A /* extends Record */ > { /** @@ -513,11 +536,8 @@ export interface DefineStoreOptions< * Optional object of getters. */ getters?: G & - ThisType< - UnwrapRef & - StoreWithGetters & - PiniaCustomProperties - > + ThisType & StoreWithGetters & PiniaCustomProperties> & + GettersTree /** * Optional object of actions. @@ -525,9 +545,9 @@ export interface DefineStoreOptions< actions?: A & ThisType< A & - UnwrapRef & + UnwrapRef & StoreWithState & - StoreWithGetters extends G ? {} : G> & + StoreWithGetters & PiniaCustomProperties > @@ -544,7 +564,7 @@ export interface DefineStoreOptions< export interface DefineSetupStoreOptions< Id extends string, S extends StateTree, - G extends ActionsTree, // TODO: naming + G, A /* extends ActionsTree */ > extends Omit< DefineStoreOptions, @@ -564,8 +584,8 @@ export interface DefineSetupStoreOptions< export interface DefineStoreOptionsInPlugin< Id extends string, S extends StateTree, - G extends ActionsTree, // TODO: naming - A /* extends ActionsTree */ + G, + A > extends Omit, 'id'> { /** * Extracted object of actions. Added by useStore() when the store is built diff --git a/test-dts/customizations.test-d.ts b/test-dts/customizations.test-d.ts index 0eeb6922..71746081 100644 --- a/test-dts/customizations.test-d.ts +++ b/test-dts/customizations.test-d.ts @@ -103,7 +103,6 @@ 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']] ) diff --git a/test-dts/index.d.ts b/test-dts/index.d.ts index 5034a82e..2e9df043 100644 --- a/test-dts/index.d.ts +++ b/test-dts/index.d.ts @@ -1,4 +1,5 @@ export * from '../dist/pinia' +// export * from '../src' export function describe(_name: string, _fn: () => void): void export function expectType(value: T): void diff --git a/test-dts/plugins.test-d.ts b/test-dts/plugins.test-d.ts index ddf68881..c3b37b6a 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, - Store, + StoreGeneric, Pinia, StateTree, DefineStoreOptionsInPlugin, @@ -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 19f702a5..ed2faa73 100644 --- a/test-dts/store.test-d.ts +++ b/test-dts/store.test-d.ts @@ -1,5 +1,5 @@ +import { StoreGeneric, defineStore, expectType } from './' import { watch } from 'vue' -import { defineStore, expectType, Store, GenericStore } from './' const useStore = defineStore({ id: 'name', @@ -205,12 +205,12 @@ noA.notExisting // @ts-expect-error noG.notExisting -function takeStore(store: TStore): TStore['$id'] { +function takeStore(store: TStore): TStore['$id'] { return store.$id } export const useSyncValueToStore = < - TStore extends Store, + TStore extends StoreGeneric, TKey extends keyof TStore['$state'] >( propGetter: () => TStore[TKey], @@ -247,12 +247,13 @@ useSyncValueToStore(() => 2, noA, 'myState') takeStore(noG) useSyncValueToStore(() => 2, noG, 'myState') -declare var genericStore: GenericStore +declare var genericStore: StoreGeneric // should not fail like it does with Store expectType(genericStore.thing) expectType(genericStore.$state.thing) takeStore(genericStore) useSyncValueToStore(() => 2, genericStore, 'myState') +// @ts-expect-error: this type is known so it should yield an error useSyncValueToStore(() => false, genericStore, 'myState') useSyncValueToStore(() => 2, genericStore, 'random')