Search code examples
typescriptmobxmobx-state-tree

Typed generic action in MobX-State-Tree


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

Solution

  • 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", "foo@bar.com");
    // TypeScript gives us an error!
    formModel.set("firstName", "baz");