Branded Types 7 exercises
solution

Add Branded Types to Functions and Models

There are a couple of parts to the solution.

Add Brands to the Getters

We definitely need brands added to getUserById and getPostById to make sure that the things we're checking are really UserId and PostId.

Before they were strings:


const getUserById = (id: string) => {

Loading solution

Transcript

0:00 There are a couple of different solutions here. The main idea is where you put these brands in order to make sure that everything you've got is safe.

0:10 We definitely know that we need to put them on the function getUserById and getPostById. We need to make sure that the thing we're checking here is a UserId and is a PostId down here. This is cool because it means that we can't accidentally pass the wrong thing to the wrong thing here.

0:28 The other interesting thing is this UserId here, which is a string, is being compared to something which is a UserId. You can actually compare these branded types to non-branded types. The question is whether you actually want to put these on the models themselves, whether you want User to be a UserId and Post to be a PostId.

0:52 You notice that this does add a couple of errors down here, which means we would have to add this as a UserId and add this as PostId here.

1:04 This, I think, is probably the correct way to model it, because imagine if you had a Post here and you wanted it to have a UserId, then you would probably want to represent this as a UserId itself, so you'd have to add it down here, UserId 1 as UserId.

1:23 I actually ran into this really hard when I was building a production application that had lots of different entities with several different ways that you could access those entities by ID.

1:34 There was a serializable version of the ID and a non-serializable version of the ID, because we were saving it to local storage but still had to have some sort of sense of uniqueness when we were not in local storage. Anyway, complicated.

1:47 Having functions like getBySerializableId or getByUniqueId, those were really interesting to have as different branded types, because it meant that you couldn't accidentally access the wrong one.

1:59 Which is why I included this, because it's a really incredibly useful pattern to know, especially if you have complicated data structures that all rely on each other and you don't want to accidentally mess things up.

2:10 Now, of course, if we call it with the wrong thing, then we're going to get this really quite nice error here, so types are branded or incompatible type, PostId is not assignable to type UserId. You get really nice errors off this.

2:22 Again, this is another application of branded types, and this should give you a good sense of what the trade-offs are, because when you have this database here and you're mocking these data, it's going to be quite annoying to have to add all of these as UserIds, as PostIds.

2:38 If you have a system with a lot of mocking, then maybe this isn't for you, but then again, you can just create a function which creates these IDs for you.

2:47 This is a really useful application of brands, for sure, and it's definitely something that I've run into in production before.