The Weird Parts of TypeScript 13 exercises
solution

Techniques for Iterating Over Object Keys

Let's look at both looping approaches to solve this challenge.

Solution 1: Using Object.keys().forEach()

The first approach is to use Object.keys().forEach() to iterate over the object keys. Inside the forEach callback, we'll access the value of the key by using the key variable:



Loading solution

Transcript

00:00 Okay, let's give this a go. I think the first way that comes to mind for me to do this would be object.keys user and then forEachKey, and then inside the key we're going to say console.log user and then key here. And let's check if this is working on the runtime level. Yes, it is. Fabulous.

00:19 So we're logging one and then VACAS into the output there. Lovely. But we have a type error. And the type error basically says user.key. Element implicitly has an any type because expression of type string cannot be used to index type user. Okay.

00:37 The issue that's happening then is nothing to do with the console.log. Actually, it's just because we're accessing user.key here. And user doesn't have an index signature. So we could say index string. I'm not sure if this would even work. Is any? Would this work? Okay. Now, because it has an index signature that can be accessed with string,

00:56 then it's perfectly okay to do it. But it doesn't. And so the reason that this is not working is because if this was typed as ID or name, then it would actually work. So we could say, let me just add the console.log back in.

01:13 One way to get around this, console.log user.key, and then we can say as key of user, like this. So now, key of user is going to be a union of ID or name here. And by using as, we're able to basically say to that key,

01:31 okay, you're not a string anymore. You're actually a more precise string. If we just extract that out, type user keys is key of user. Just want to check if this will work. It doesn't actually give us the readout. But if I say console.user.key is user keys,

01:48 then it will tell me that I have ID and name to select from here. So this is one way around it. Let's look at a couple of other solutions. Another solution would be to widen the type inside this user, inside the print user function. So here instead, we have a record string any.

02:06 Because if you think about it, actually we want to map over every key that we get from what we're receiving. So it doesn't matter that this is a user. In fact, we might even be able to rename this just to be an obj. And we could rename print user into print obj values like this.

02:24 Now, basically we've made our function a bit more generic really, because it didn't really matter that this was a user. And it works on the type level and it works at the runtime level. Because we know that we can console.log the obj.key here, and we're going to get something back from it.

02:42 So that's another solution. Another solution is to use a for loop here. But a for loop has the same issue. The for loop basically says, okay, this is still a key here. So this is another way that TypeScript loosens things up.

02:56 But because of excess property checking means that this can't really be a tight type. So we end up with console.log user key as key of user. Or, of course, we could do the same thing with this for loop, where we could say actually the thing that's being passed in is an obj, not a user.

03:14 So you've got two choices there really. You can either make the key kind of like slightly unsafe and patch it that way. So make the key access really tight. Or you can make the type that's being checked, that's being indexed into, looser. So you can either tighten one end or loosen the other end.

03:33 Can't really do anything else. But that's really, I'd say, a good mental model when you're working with iterating over object keys in TypeScript.