Use 'extends' keyword to narrow the value of a generic

You can use the extends keyword to narrow the scope of a generic to make it more useful. Here for instance, we want to get a deep value from an object


const result = getDeepValue(obj, "bar", "d")

getDeepValue is gonna take in the object and then take in the two keys that it's gonna use to then grab the value. So we can use extends here to say the FirstKey needs to be keyof Obj, which is our object.


export const getDeepValue = <Obj, FirstKey extends keyof Obj, SecondKey>(
obj: Obj,
firstKey: FirstKey,
secondKey: SecondKey
) => {
return {} as any
}

And now we get some nice auto-complete on this deep value. Which is either foo or bar. Let's say it's bar for now.


const result = getDeepValue(obj, "bar", "d")

Now this second key is now going to extends keyof Obj[FirstKey].


export const getDeepValue = <
Obj,
FirstKey extends keyof Obj,
SecondKey extends keyof Obj[FirstKey]
>(
obj: Obj,
firstKey: FirstKey,
secondKey: SecondKey
) => {
return {} as any
}

And that means that when we select bar, this third argument is going to be c or d.


const result = getDeepValue(obj, "bar", "d")

And when we select foo, it will be either a or b.


const result = getDeepValue(obj, "foo", "a")

So let's say it's a for instance. We now need to work out what the result is going to be, because it's currently typed as any. So here in the return type definition we can just say Obj[FirstKey][SecondKey] just like property access in JavaScript.


export const getDeepValue = <
Obj,
FirstKey extends keyof Obj,
SecondKey extends keyof Obj[FirstKey]
>(
obj: Obj,
firstKey: FirstKey,
secondKey: SecondKey
): Obj[FirstKey][SecondKey] => {
return {} as any
}

Now the result is a Boolean. And if I change the third argument to b, then it becomes a number.

Transcript

0:00 You can use the extends keyword to narrow the scope of a generic to make it more useful. Here, for instance, we want to get a deep value from an object, and getDeepValue is going to take in the object and then take in the two keys that it's going to use to grab the value.

0:15 We can use extends here to say the FirstKey needs to be keyof Obj, which is our object. Now, we get some nice autocompletes on this deep value, which is either foo or bar. Let's say it's bar. This second key is now going to extends keyof Obj, FirstKey. That means that we get this bar. When we select bar, this is going to be C or D. When we select foo, this is going to be A or B.

0:47 Let's say it's A, for instance. We now need to work out what this result is going to be. It's currently typed as any. Here, we can say Obj, FirstKey, SecondKey, just like a property access is JavaScript. The result is a Boolean. If I change that to B, then it becomes a number.

The extends keyword is very powerful in TypeScript. Here, I use it to narrow the value of a generic to enable some beautiful autocomplete/inference.

Discuss on Twitter

More Tips

Assign local variables to default generic slots to dry up your code and improve performance