How to Make One Function Argument Dependent on Another in Typescript
Nick Scialli
September 05, 2020
Typescript can be incredibly helpful when we use some of its more advanced features. In this post, we specify the type of one function argument based on the type of another.
A Scenario
Let’s say we have a change handler for an object’s properties. In plain JavaScript, this change handler might look something like this:
const user = {
name: 'Daffodil',
age: 27,
admin: false,
};
const changeUser = (key, value) => {
user[key] = value;
};
Simple enough! But what if we want to do something similar in Typescript?
A Naive Approach
Let’s take a naive approach. We might assume the key
will be a keyof
our User
type and the value
might be something like User[typeof key]
.
type User = {
name: string;
age: number;
admin: boolean;
};
const user = {
name: 'Daffodil',
age: 27,
admin: false,
};
const changeUser = (key: keyof User, value: User[typeof key]) => {
user[key] = value;
};
Alas, this doesn’t work. We get an error like this:
Basically, the Typescript compiler doesn’t quite understand how to narrow the type of value
based on the provided key
.
The Solution: Generics
The solution to our problem is Generics! We can specify a generic, K
, that is a keyof User
. Then, our value
can be of type User[K]
.
Here’s how this works:
type User = {
name: string;
age: number;
admin: boolean;
};
const user = {
name: 'Daffodil',
age: 27,
admin: false,
};
const changeUser = <K extends keyof User>(key: K, value: User[K]) => {
user[key] = value;
};
Great! We no longer have a compilation error and our value
argument will now be narrowed based on the key
argument. To see this in action, let’s take a look at different scenarios where our compiler does and doesn’t get mad at us.
We see that our Typescript compiler is okay when we specify the correct types for each key, but it shows errors when the wrong types are provided for a particular key!
Nick Scialli is a senior UI engineer at Microsoft.