Generic set action in Typescript
July 21, 2019 at 7:11pmGeneric set action in Typescript
July 21, 2019 at 7:11pmHi, does anybody know how to make something like this work? For quickly prototyping it would be nice to have a typesafe set method.
import { Instance, types } from 'mobx-state-tree';const storeFields = types.model({text: types.string,number: types.number,bool: types.boolean});type IStoreFields = Instance<typeof storeFields>;const store = storeFields.actions(self => ({set(key: keyof IStoreFields, value: IStoreFields[typeof key]) {self[key] = value; // TS-Error: Cannot assign to '[$stateTreeNodeType]' because it is a read-only property}}));
In JS it's no problem until it explodes at runtime because of an invalid combination of key and value.
July 21, 2019 at 9:27pm
You can use
Object.assign(self, { [key]: value, })
. However, Object.assign
's return types are surprising: https://stackoverflow.com/questions/39906054/typescript-object-assign-confusion
Personally I stopped using Object.assign
for this very reason and I prefer strongly typed individual setters. Here's one way to do it:set({ text = self.text, number = self.number, bool = self.bool, }) {self.text = textself.number = numberself.bool = bool}
Yes, it is verbose and repetitive, which sucks. But at least it is type safe.
November 29, 2019 at 10:45am
Hi ,
i was facing the same issue and i could solve your "read-only" error, simply by using
type IStoreFields = SnapshotIn<typeof storeFields>;
instead of "Instance" interesting. Which typescript version do you use? And what are your settings in your tsconfig?
Because I tried to replicate this and got this error
i double checked my test case and it seems to make a difference if the model has a single type of types (eg: types.string) or different ones, like in your sandbox.
i created a sandbox on my own and tried to figure it out and it seems to work now for multiple types. take a look and tell me if this may fix your problem: https://codesandbox.io/s/pensive-mahavira-gh7lw?fontsize=14&hidenavigation=1&module=%2Fsrc%2Findex.ts&theme=dark
for the tsconfig i added the recommended flags from mst https://github.com/mobxjs/mobx-state-tree#recommend-compiler-flags
oh damn! thanks for checking! ... i think we're coming closer! https://codesandbox.io/s/green-wave-myouu?fontsize=14&hidenavigation=1&module=%2Fsrc%2Findex.ts&theme=dark
November 30, 2019 at 4:05am
November 30, 2019 at 12:11pm
, do you want to write about it? Else I would write a blog post about it and of course give credit to you and link to this discussion.
December 2, 2019 at 8:58am
December 5, 2019 at 3:49pm
And there was another issue. Your solution didn't support optional types. I managed to use it with SnapshotIn but had to tell Typescript to ignore the line
self[key] = value
December 7, 2019 at 7:29am
December 8, 2019 at 8:09am
The problem is in the
types.optional
- it accept a string or undefined but will be inferred as only string. Of SnapshotIn
, SnpshotOut
and Instance
only SnapshotIn
describes the types to set something correctly.This looks a little bit less brute force:
.actions(self => ({set<K extends keyof SnapshotIn<typeof self>,T extends SnapshotIn<typeof self>>(key: K, value: T[K]) {self[key] = value as any;}}));
Even if it as basically the same. We tell the compiler, it's okay.
February 3, 2020 at 3:18pm
Hi, finally managed to write the article about it
November 5, 2020 at 8:10am