pinia icon indicating copy to clipboard operation
pinia copied to clipboard

Cannot bypass getters with TypeScript

Open nterray opened this issue 3 years ago โ€ข 5 comments

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

nterray avatar Aug 30 '22 12:08 nterray

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

LinusBorg avatar Aug 30 '22 12:08 LinusBorg

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

nterray avatar Aug 30 '22 13:08 nterray

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
              ~~~~~~

nterray avatar Aug 30 '22 13:08 nterray

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

posva avatar Aug 30 '22 22:08 posva

Can we have an update for the docs at least to mention this issue?

Hatzen avatar Sep 07 '23 11:09 Hatzen

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);

sarahsporck avatar May 28 '24 16:05 sarahsporck

I forgot to update the docs with the Getters, it's fixed now! See 7218691

posva avatar May 29 '24 07:05 posva