DEV Community

Cover image for Deep Object Change Handlers in Typescript
Nick Scialli (he/him)
Nick Scialli (he/him)

Posted on • Originally published at typeofnan.dev

Deep Object Change Handlers in Typescript

Typescript's ability to deeply-type objects is incredibly handy: it gives you confidence that you're accessing the right keys on an object and that you're using those keys as the right types. However, this typing doesn't come for free: it can add complexity to things like change handlers. In this post, we'll write a deep object change handler that both allows us to specify deep object types and satisfies the Typescript compiler.

A Sample Deep Object Type

Let's use the following Settings type as an example. It contains some visual settings about our app and some information about our user.

type Settings = {
  display: {
    mode: "light" | "dark";
  };
  user: {
    name: string;
    age: number;
    admin: boolean;
  };
};
Enter fullscreen mode Exit fullscreen mode

We can then create a sample object that satisfies this type. Let's use the following example.

const settings: Settings = {
  display: {
    mode: "dark",
  },
  user: {
    name: "Betty",
    age: 27,
    admin: false,
  },
};
Enter fullscreen mode Exit fullscreen mode

Writing a Change Handler

So what if we want a change handler that will change any property two levels deep in this object? The secret lies in the use of Generics. We can specify that our key is of type K, where K extends keyof Settings. Likewise, we can specify that our subkey is of type S, where S extends keyof Settings[K].

Putting this all together, we get the following change handler!

const updateSettings = <
  K extends keyof Settings, 
  S extends keyof Settings[K]
>(
  key: K,
  subkey: S,
  value: Settings[K][S]
): Settings => {
  const newSettings = {
    ...settings,
    [key]: {
      ...settings[key],
      [subkey]: value,
    },
  };

  return newSettings;
};
Enter fullscreen mode Exit fullscreen mode

And there we have it: a framework by which we can update deep types and keep our Typescript compiler happy!

Top comments (0)