Typed generic action in MobX-State-Tree

744 Views Asked by At

You can modify a MobX-state-tree node with the help of actions. This is generally great, but it can become cumbersome if you have a lot of different properties that you want to set, e.g. if you have a model representing a form.

Is there a way to write a generic action that takes a property name and a value as arguments, and sets the property to the value on the node, while still having TypeScript verify that the arguments are valid?

Example

import { types } from "mobx-state-tree";

const FormModel = types
  .model({
    email: "",
    password: "",
    username: ""
  })
  .actions((self) => ({
    // This becomes cumbersome as the model grows
    setEmail(email: string) {
      self.email = email;
    },
    setPassword(password: string) {
      self.password = password;
    },
    setUsername(username: string) {
      self.username = username;
    }
  }));
1

There are 1 best solutions below

0
On BEST ANSWER

As outlined by pixelkritzel in his great blogpost we can create a generic action with the help of two generic types and cast.

Example

import { cast, SnapshotIn, types } from "mobx-state-tree";

const FormModel = types
  .model({
    email: "",
    password: "",
    username: ""
  })
  .actions((self) => ({
    set<
      K extends keyof SnapshotIn<typeof self>,
      T extends SnapshotIn<typeof self>
    >(key: K, value: T[K]) {
      self[key] = cast(value);
    }
  }));

const formModel = FormModel.create();

// Works!
formModel.set("email", "[email protected]");
// TypeScript gives us an error!
formModel.set("firstName", "baz");