Pinia: prohibiting implicit state mutation
One of the main reasons to use TypeScript is predictability. Any data change must be intentional: we either call a function or update ref
. But in the case of Pinia, the following code is possible:
const store = useDummyStore();
store.stateValue = null; // ❌ direct mutation of state, but no error
Explicit vs implicit mutation
What's wrong here?
- ❌
store.stateValue = null
— looks like an object field, but actually mutates the global store directly. - ✅
storeToRefs(store).stateValue.value = null
— requires a preceding step,storeToRefs
call, and operating on.value
, which makes the intention obvious.
This behavior is closer to commit()
from Vuex: we don't just assign something, we ask the store to edit its state
. It is this intentionality that separates good architecture from fragile architecture.
Pinia and implicit mutations
Unlike Vuex, Pinia allows you to directly change the state
by default. There are no mutations
or built-in restriction. Strict mode was discussed back in 2020, but it has not been implemented.
How to prohibit implicit mutations
We can regain control through typing. Make state
read-only:
// env.d.ts
declare module "pinia" {
export interface StoreDefinition<
Id extends string = string,
S extends StateTree = StateTree,
G = _GettersTree<S>,
A = _ActionsTree
> {
(pinia?: Pinia | null | undefined, hot?: StoreGeneric): Store<Id, Readonly<S>, G, A>
}
}
Now:
// Direct assignment
const store = useDummyStore();
store.stateValue = null; // ❌ Error: Cannot assign to 'stateValue' because it is a read-only property
// Via storeToRefs
const { stateValue } = storeToRefs(useDummyStore());
stateValue.value = null; // ✅
// Via action
store.setValue(null); // ✅
Pinia offers flexibility, but with that comes vulnerability. If you want state
changes to always be intentional and controlled, setting Readonly<StateTree>
is a good starting point. We are not limiting the capabilities of Pinia, we are simply bringing back a clear discipline that is especially needed in a team or on a long-term project.