Identity Functions 5 exercises
solution

Constraining and Narrowing an Identity Function

We’ll start the solution by annotating TFruits with const like before. This makes sense because it's like we are passing in a read-only thing like before.


export const narrowFruits = <const TFruits>(t: TFruits) => t;

The next step is to constrain the shape of things coming i

Loading solution

Transcript

0:00 The solution is a little bit nasty. You probably all got to a certain spot, which we need to add a const here. Const makes sense here, because we're trying to get this readonly thing passed in.

0:14 You can see the test is passing, whoo, except not quite. There's a test down here that isn't passing, because we're expecting notAllowed to not be able to be passed in and it is allowed to be passed in.

0:26 You think to yourself, "OK, we need a generic constraint." We say extends and we know the shape of the thing that we're passing in. This is pretty simple to type. We just say name: string and price: number. Now, that's pretty good.

0:39 Now, the bottom test is passing, but the middle test or the top test isn't. Why is that? Well, fruits is now being inferred as this mutable array, name: string, price: number.

0:54 I think we need to be a bit more precise with this constraint, because what we're saying is technically name, price: number can be any length. This can be an array of any length. We need to manually specify here that it's a tuple. The way we do that is two ways. You can specify it as readonly.

1:14 What you see here, if we remove this constraint, this will give you a clue about what's going on here. NarrowFruits here, you can see that it starts with a readonly annotation here. That readonly is at the start of this tuple here. Readonly tuple means that you can't add to it.

1:34 By adding this here, we're basically saying, "OK, this is a tuple." It's a little bit annoying. I have to say that it doesn't pick this up automatically or make that inference automatically, but this is the world we live in.

1:47 There is another solution here, which is instead of using this readonly tuple syntax, you can use ReadonlyArray type helper. This means you don't need to add this thing on the end, because it's already an array and fruits will be properly narrowed using that. ReadonlyArray captures that readonly element in it and looks a little bit more readable to me.

2:08 I mean, this syntax is fine too. If we capture this out into its own type and we say FruitsConstraints, let's say and add it there. Then we can say, and these needs to then extends FruitsConstraints.

2:20 Then we can say, const whatever is a FruitsConstraint. It's going to force us to add name: string and price whatever, price: one. We won't be able to push to it from there. This is just saying it's a ReadonlyArray.

2:36 Now by itself, of course, we don't capture the literals in it. Which is why this identity function is so important, because it means that we can constrain the type of config that's coming in, but also infer it to its literal. Very useful.