Deep Object Change Handlers in Typescript
Nick Scialli (he/him)
Posted on May 22, 2020
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;
};
};
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,
},
};
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;
};
And there we have it: a framework by which we can update deep types and keep our Typescript compiler happy!
Posted on May 22, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.