TypeScript 3 Fundamentals, v2 Unknowns & Branded Types
This course has been updated! We now recommend you take the TypeScript Fundamentals, v3 course.
Transcript from the "Unknowns & Branded Types" Lesson
>> Mike North: When working with unknowns, going back to that topic for a moment. There's one thing about this that kinda bugs me, and that is illustrated by this example. So we have a value aa, which is unknown, bb, another unknown, but a completely different type. And it's actually like we're not getting a type error here.
[00:00:25] So any unknown can be assigned to any other unknown. And it's because it's a top type. It's just like an any, it's sort of an opaque any. We can't see into it but it can hold whatever it wants. So it's very easy to get a couple sort of different opaque values mixed up and intertwined with each other.
[00:00:45] So, I'm gonna give you an alternate solution that for me it's a little more involved but the idea is a lot safer and a lot more intuitive. And this is called branded types. So I'm just gonna create an interface that has a special property name that is very unlikely to overlap with anything any other object might have.
[00:01:12] And I have a function called brand and unbrand. What I'm doing here is, typically this would be unsafe, all right? This is called casting, and it is where you're forcing a value to be regarded as a particular type. So I'm passing a string in here, and I'm forcing it to a top type unknown, or you could do any here.
[00:01:37] And then forcing it to BrandedA. If we did not do this intermediate step we'd be all that because there is no overlap between this types. We're making a string look to type script like an object with a special property on it. And then we have another matching function that does the inverse transform.
[00:01:57] So we can pass this things around and they dont look like strings. Nobody is ever gonna be able to use this like a string, here is the advantage. So number one, I wanna make sure we agree it's opaque, right? We don't see that this is a string, we can't do string stuff with it.
[00:02:16] But if we had another type, and this is just the same thing, just a different property key and value.
>> Mike North: We can create branded values, right? BrandB takes an object with a property called abc, just to illustrate. You can do this with whatever type you want.
>> Mike North: But this is the appealing part.
[00:02:45] Because these are two different branded types with two different properties. Here this one ends with A, this one ends with B. You can never get them confused. They're two different structures. There will not be a structural match between these. So we've hidden away the original identity of what this value is.
[00:03:08] We've made it so that they're not interchangeable. You can't accidentally mix the unbranding functions, which will recover the original value. But you can get back to your original values through the unbrand if you do it correctly. So, it's almost like taking a value and putting it in an envelope and packaging it up.
[00:03:29] It's like those envelopes you'd mail an important document in. Where you can't see the contents, but it sort of has a stamp on it, so you know this came from that person and only they can open it back up again. So, I'll get to your question in a second.
[00:03:46] My only tip here is, make sure that there's only one place in your code where you're doing your branding and your unbranding. That is the way you can be absolutely sure nobody is peeking inside this thing, or accessing trying to access its original value.
>> Mike North: Having the cast in one direction to the opaque structure and back in the same job descriptor module.
[00:04:12] That allows you if you wanted to change things up, you could remove this property key and you could make changes in one location. And because nobody else can interact with it in a meaningful way, you shouldn't have to alter much of anything else. Did you have a question?
>> Speaker 2: Yeah, what is the point of doing it this way versus using an object and doing a private inside of it?
>> Mike North: Privates can only be used on class instances.
>> Speaker 2: Okay, so this was?
>> Mike North: So, I mean, you could do things that way, but that would mean that the class would have to expose a way of getting that original value back out.
[00:04:55] So I use this, in particular, for ID generation. Where if I've got a dozen different type of entities in my app, and I'm issuing IDs, and I wanna make sure they're all coming from My ID generator, it's not just any string it's this. Now, I will create a branded type.
[00:05:13] So you can be sure like this is the origin. It's the only function in the whole app that will return to type with this structure. When it comes time to serialize this or that to JSON, this here is a lie. This is the real type underneath, and whenever all of the types compile away, your code will run exactly as it would otherwise run, but this is just a way of saying hands off this thing.
[00:05:38] Like I'm gonna use the type system as a way to hide the nature of this value from anyone who would care to try to poke at it. And again, it's not about security. It's about discouraging developers from intertwining themselves with private parts of your code. As a library author I want to make sure that I can cut new releases with changes to my code.
[00:06:06] And I'm not gonna break a lot other people. And this is a way of sort of drawing a clean line, don't use this, I'm deliberately making it difficult.