Fix TypeScript Errors related to Function Type Parameters and Generic Constraints
Learn how does TypeScript resolve function type parameters and generic constraints - and fix related compiler errors.
Transcript
Let's see how the function type parameters work with generic constraints in TypeScript. For this reason I have created a user object type and we'll create declare a const user variable of type user which we're going to use. Let's also create a simple prop function which is going to access the key of an object. So let's return object with the index of key. This function used to be in the TypeScript docs, not sure about now, so it's pretty well known.
Anyway, it's a very good example of how do type parameters work. So let's put a type parameter t which declares what t is going to be. Let's also create what the key could be. So if we write string, TypeScript is not going to compile since it will say no index signature with a parameter of type string was found on type unknown. So we don't, first, we don't know what T is going to be because it might not be an object and otherwise TypeScript cannot guarantee that there would be all potential strings available on whatever T is.
So we'll write key of T so that whatever that is, whether it's a primitive or object, we can basically access the keys over there. By the way, what are keys over primitives? For instance, if we have a number, it could be basically out of boxed to the object version of a number. So this is what would automatically happen. So let's call prop on the user and let's invoke it with an ID and Let's assign it to the ID variable and let's check what is the type of that expression.
So what we see is that it could be a string, but it could also be an object which is clearly wrong because what we have under ID should be clearly a string, so this doesn't work. It could be even worse if we write, if we want to access what is for instance a country or a city. So let's say what is a country. So this should be a prop of the address property and using this one we could also when we walk into the address we would like to walk into the country. So what we can see at the line above in the ID variable is that the type is too wide and in the country line it basically doesn't work, it doesn't compile.
So what is the issue? When we have a function that accepts one type parameter, as we can see here over type T, this is fine for T, but there is no type parameter for key over here. And this is important when we take a look at each of the invocations. So when we take a look at the prop invocation over here we can see that for this exact line TypeScript knows that the type is user but How could it make the ID precise if it doesn't know what is the exact key that we are trying to walk into? So basically, we know that we are passing ID over here, but this information is lost on the level of the compiler.
Again here we know that this should be an address so that we would get limited down to the properties over this object but this information is clearly lost. So what we can do is basically uplift this information from here to basically here so that K extends so it has to be anything but from within this constraint right so we pretty much haven't changed anything when it comes to what we can pass over here, right? But whenever we invoke any function, now on the compiler level, not only runtime level, but on the compiler level, for each invocation TypeScript knows that this is clearly about the ID. So when we walk into user of ID, this is very clear that it has to be a string. When we walk into address, it's very clear that this is going to be a nested object, the property under address.
And finally, walking from within the address into country this has to be whatever is under user.address.country