pinia
pinia copied to clipboard
Cannot bypass getters with TypeScript
Reproduction
https://stackblitz.com/edit/github-xe2cxw?file=src%2Fstores%2Fcounter.test.ts
Steps to reproduce the bug
Given the following code:
import { defineStore } from 'pinia';
import { createTestingPinia } from '@pinia/testing';
const useStore = defineStore('lol', {
state: () => ({ n: 0 }),
getters: {
double: (state) => state.n * 2,
},
});
const pinia = createTestingPinia();
const store = useStore(pinia);
store.n++;
store.double = 3;
Expected behavior
TypeScript should not complain.
Actual behavior
TypeScript raises the following error:
TS2540: Cannot assign to 'double' because it is a read-only property.
Additional information
This is to be able to mock getters in tests https://pinia.vuejs.org/cookbook/testing.html#mocking-getters
Why would you assign to a getter? It's a getter, it has not setter.
TS is warning you correctly here. if you want to do this for some exotic reason: //@ts-expect-error
Sorry I forgot to mention that it was to be able to mock getters in tests https://pinia.vuejs.org/cookbook/testing.html#mocking-getters
By the way TypeScript complains about this error for Pinia's test file :
โ ./node_modules/.bin/tsc packages/testing/src/testing.spec.ts
[โฆ]
packages/testing/src/testing.spec.ts:217:11 - error TS2540: Cannot assign to 'double' because it is a read-only property.
217 store.double = 3
~~~~~~
I think we made the getters read only at some point because its easier to spot bugs. A possible solution for this is to have some kind of wrapper when calling useStore but still not ideal. At the moment, a ts-expect-error comment in tests will do but maybe we can come up with a more ergonomic solution
Can we have an update for the docs at least to mention this issue?
I adjusted the mockedStore implementation in the docs & made the getters writable in typescript. This seems to work quite fine.
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
function mockedStore<TStoreDef extends () => unknown>(
useStore: TStoreDef,
): TStoreDef extends StoreDefinition<infer Id, infer State, infer Getters, infer Actions>
?
| Exclude<
Store<
Id,
State,
Getters,
{
[K in keyof Actions]: Actions[K] extends (...args: infer Args) => infer ReturnT
? // ๐ depends on your testing framework
Mock<Args, ReturnT>
: Actions[K];
}
>,
_StoreWithGetters<Getters>
>
| Writeable<_StoreWithGetters<Getters>>
: ReturnType<TStoreDef> {
return useStore() as any;
}
const mockedCounterStore = mockStore(useCounterStore);
I forgot to update the docs with the Getters, it's fixed now! See 7218691