Improving Type Inference with Additional Generics
Before we get to the solution, let's recap where we started.
In the starting point code, if we hover over getValue
we can see the object being inferred:
// Starting Pointconst getValue = <TObj>(obj: TObj, key: keyof TObj) => { return obj[key];};// hovering shows:const getVal
Transcript
0:00 The reason this is happening is because, if we hover over getValue, you can see that this object here is being inferred. The key here is actually being inferred as a or b or c. If we look at the result here...Let's say we have Result. We say, "typeof obj." We're going to index into it with a or b or c.
0:27 We've seen this before. We know how this works, typeof a or b or c. It's going to be string or number or boolean. If I add d is new Date, then what we get is d is going to be string or number or boolean or Dates. We can see here then that this key, when we index into it using obj.key, then this doesn't actually represent a specific key. It represents a union of all of the possible keys.
0:58 How do we fix that? We need to infer a specific key at this point, which means we need a second generic. That second generic is going to live just here. It's going to be TKey. We can say TKey is right there.
1:15 Now, TKey cannot be used to index type TObj. Of course, because we could actually now pass in anything here. We've lost our beautiful autocomplete. We need to constrain this type. The way you constrain a generic type is with a constraint.
1:30 We say, "TKey extends keyof TObj." Now, we get our beautiful autocomplete back. numberResult,results in number. stringResult results in string. booleanResult results in boolean. That's because what we're doing is we're indexing it with the thing that we capture in this type argument.
1:49 What we've got here, is we're now capturing to type arguments, the object that's passed in and the thing that's used to index it. We know that when we get to here, then this is actually like indexing with the specific key. We can make this doubly sure by saying, "TObj TKey" here.
2:09 Now, it makes sense. We're actually accessing the specific object with the specific key that we infer from that generic type.
2:21 The lesson here is to make sure that if you're not getting the inference you want, make sure that you haven't got any missing generics.
2:30 You need to make sure that the right things are being inferred at the right times and that you've got the thing that you want to infer there so that you can then, in your return type or even, as we have here, an implicit return type, make sure that the types can do what they're supposed to do and they have all the information that they need to operate and give you the correct type back.