Branded Types are a MUST for Scalable Apps | Nominal Types in TypeScript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I've seen a lot of people make this mistake especially in production environments so please don't do this and that is let's say you have a server or even a react application and you need to handle state so you need to remove things from the database or from the cache in the case of react you need to update records you need to do a lot of Transformations so let's take this example we have a function check user permission Tak in a user ID then the permission and let's say we query for something to see if the user is authorized to perform that action and then we have another function get post details so we pass in a post ID we do a query to the database get all of that information for the post so in this case we're just returning an ID and the title and then we have a delete post function so again we take a post ID and we just do a delete from xtable and call it a day but let's say we have a Handler like this one so we take in the request we take in the context which again is a session with a user ID so we have a middleware somewhere that is going to retrieve the session from the authorization Heather and it is going to give us back the user ID that pertains well to that particular session so here all we do is retrieve the post ID so we say url. search pam.get the post ID then we retrieve the user ID then we check if the user has permission if they don't have permission we just return a new response that is 403 which means forbidden then we get the post details we passing the post ID and we check if the post exists if there is no post we cannot delete a post so we return a 44 post not found and finally we have delete post but we pass in a user ID now as far as typescript is aware delete post takes in a number and user ID is a number so this is completely fine but what if your database has Auto incrementing integers or the IDS now you could potentially be passing the user ID let's say 8 and we're deleting the postate which could pertain to a completely different user so now that user is going to open up the application and they cannot find that post anywhere so as you can see it is very problematic now of course just by reading the code we know this is a user ID and it doesn't really make sense to pass in a user ID to delete post I mean depends on the architecture but generally You' pass the ID of a post and you could say well just change this to post ID but again what if C piloted this for you or you needed to push this out to production as fast as possible you've now introduced a bug so this is where nominal types come into play which are also known as branded types in typescript and that is you assign a unique property to a type so that you can distinguish types that share the same underlying type so we know that a post ID is a number and a user ID is a number so we can say type user ID is equal to number and then at the type level you add these unit property now you will not be able to access this in the run time because you're not assigning this property to a user ID but as for the type level it is going to be really useful so now you can say user ID post ID and then you can replace the user ID like this you can use the post ID in these two functions now here in the user ID this is going to be again a user ID and by doing this if I scroll down we're now getting two errors or getting an error here saying post ID is not assignable to parameter of type post ID and why because post ID is just a number it doesn't contain this property so what you need to do in this case is say this is going to be a post ID so now you have asserted this now you need to make sure that this is true and now we get rid of the error but now here in line 54 we're getting an error because well the types do not correspond with we have a user ID and we're trying to pass it over to a post ID so now it's very easy to see where we're going wrong and we can just change this to be the post ID as simple as that now what if you want to enforce this at the runtime as well not just at the compile time well for that I would recommend using this great library and that is import effect from effect so go ahead install this Library this one is going to give you a utility which is brand now what you can do here is you can define a refined brand so you can say const and then user ID is equal to Rand and then you say refined and then you pass in the type for the brand so in this case you can say number and then an intersection and then here you say brand. brand so this is a utility type this is an interface and then you pass in the identify for this brand now why do you need to do this because effect needs to somehow identify your brand and that is when you're piping things and whatnot which I'll show you in a moment and now here what you can do is you get access to the numbers so you can say first of all number must be greater than zero because it is an auto incrementing integer you know that it is going to start at zero and in the case that it is not valid you get not valid or invalid number and then you can say brand. error and then you can pass in a custom error whatever you want so this is the onfailure so you can say invalid number is not a valid user ID and now we can get rid of this type but to extract the type you can say user ID and then you can say brand that and then you can say from Constructor and then you pass in type of user ID so now you have the type level for the brand and you have the runtime brand so we can do the same for the Post ID so const post ID we do the same and then we use from Constructor and assign the type now how can you use it in the runtime well for that you can say const post ID and then you pass in the post ID and as you can see the resulting type is a number and brand. brand post ID now if this validation fail so this one right here it is going to throw that error now notice how this isn't particularly very useful because when you over over it type you can see okay it's a number and brand. brand post ID but what if you want to get the type alas because you see okay so the post ID is going to be a number and brand. brand post ID let's create a function that is going to accept this but now we're going to be a little bit lost we're going to to say should I copy this data type paste it in the argument of the function or should I extract this to its own typ alas well for that you can just come here and instead of saying from Constructor which is like the easiest way you can just say number and brand. brand and then you say post ID and then you can replace this generic and then you say post ID and you can do the same for the user ID and now when you you come here and overover over the variable you know that it is a post ID so now you know that you can maybe import this type alas but notice how we're doing this number because we need to convert it to a number first and then we need to check if it's greater than zero but we're not checking if it is actually an integer it could be a decimal we can refine this even further so for that you can extract another brand so you can say in or integer or you could say integer whichever you prefer and then you say this will be a number and brand. brand integer for Simplicity purposes and then we get the number and we just check if it's an integer and if not we can say this is not an integer and then we can do the same for a positive integer so we can say const positive and then we just check if it's greater than zero so now we can come here and instead of doing this we can just say brand. all and then you pipe this through so first of all it must be an integer and then we must check that it is positive and as for the type itself now here you need to say brand. brand from Constructor type of post ID so now you have created these brands that are generic so you can construct brands from these and then you can just utilize them however you want you can say user ID and then brand. all again must be an integer and positive and then the type will be this one now of course when you hover over this well as you can see okay post ID is a number and brand. brand integer and brand. brand positive but the plus is you get this great system and you get runtime validations now what if you're using brand from effect but you want to create nominal types meaning that they are not enforced at the runtime just at the type level like we did before because realistically speaking you wouldn't really validate user ID because that's self-contained within the application and it really doesn't make sense to validate when you control the code and you can enforce that at the type level now you cannot enforce the post ID from the incoming request because anyone can send send whatever they want but for the case of your application this is just redundant so what you can do here instead is you can say con user ID brand. nominal and then you say user ID you just invoke this and then say user ID number and brand. brand and then the identifier and that's it this is for compile time so you get the type safety and this is for the runtime level so with validates at the run time now to use the user ID you do the same so you can say const user ID to and then you just say user ID passing a number and you get back a user ID as simple as that nothing too crazy now what about the client side let's say you want to have a schema like a s schema and you need to validate the incoming data how can you create a brand from the validated result well view you saw you could create a schema say powers or save Powers but then you need to map over the result and you need to replace the say the user post ID with the brand with the nominal brand So to avoid that effect actually has a module for schema so you can say effect and then it's artifact and then schema so you need to download this separately and this one brings you over schema which is everything that you see here so it has a lot now if you fetch this Endo it is going to give you back so response it is going to give you back an array of ID which I believe it's a number then to do which is like the description then completed which is a Boolean and then a user ID which is a number and then a total which is number this is for pagination ski and limit now you need to convert this to a runtime schema how can you do that well for that you can say const response schema is equal to schema and then if you've ever used something like y or S you know that it's going to be object but in this case it is going to be struck which is the same like an object and then here you pass in the fields so you can say to do and then this will be a schema. array and then here you pass in the other Str Str so you say schema do struct and then you can say ID is going to be a schema and then do number so as you can see it is quite similar to s or I would say it is almost identical at least at the fundamental level and then you say. pipe so you pipe this through same as we did with brand. all and then you say this will be an integer and then this must be greater than zero and then you can do the same for to do so string do pipe and then schema you can say schema. trimmed and then schema do and then you can say nonempty and then the completed well this one is just the schema that Boolean and then the user ID and then for the other ones again they must be integers greater than zero except for skip because skip can be zero and that's it but now how can we at the schema level tell the fact that hey user right is going to be a branded type and the IDE for it to do will be a branded type because we need to differentiate between the two well for that all you need to do is create the nominal Brands because this will be for the run time and the nominal types will be for the compile time so you can say const user ID is equal to Brand then nominal and then type user ID we can say number and brand. brand and then you say this will be a user right and then you can pass in the user ID here and then what you need to do is pipe it through again so you can say schema. brand and this is actually from Brand not brand and now when you parse this you're going to get back not only well the integer greater than zero but also the Branded or rather the nominal type but well this is for the runtime and you can do the same for a Todo ID so you can say too ID and then number and brand. brand to do ID and then you say nominal and then you can come here to ID and just say schema. from Brand and then to do ID as simple as that now how can you get back the data which is very important well for that you can just say schema and then you say decode unknown so this is not powers or save Powers this is decode unkknown because you're decoding something and that something is unknown and then you pass in the schema so you say respond on schema and then here you can pass in some extra properties so you can pass in errors and an un excess property and this is a union of error ignore or preserve so if there is an excess property we'll just throw an error basically in the case of ignore that means remove them we only want those defined here in this schema and in the case of preserve we'll leave them be we obviously do not get any type safety for those SE ex ra properties but we still have them within the object so in this case we can say ignore and now when you invoke this it is going to well return a function that's why we're invoking this again and then you can pass in the data that you want to parse so you can say test and then a string and then you have some override options that is if you want to extract this into its own generalized parser so you can say con pars or the code response schema from unknown and then you can say schema. decode unknown you do this and then you can come here and say thecode response schema from unknown you pass in the data and then here you can overwrite the properties that you defined here basically now when you do this what you're getting back is an effect now if you're not familiarized with effect you will not understand anything here but let me quickly explain what is going on so here this is going to return an effect and the first generic that it is going to return so up to here is the result so if this is successful we get access to this data now in the case that this function errors because well the data is Mal firmed or doesn't satisfy this structure here then it is going to give you back a power error and we can ignore this never and to get access to the data you just need to pipe this through and then you can say effect and then you can say that and then you can say touch and here you can say parse error because this is the only error this could result in and then you can pass in something that you want to do with this error so you get the error here but in this case let's just say okay we will succeed with null in the case that this fails and then we can say effect do run synchronous although I'm not entirely sure if this is somehow ConEd either is a promise we would have to run this code anyway result is equal to this and if we take a look we get a read only so that's great we're promoting immutability and as you can see the ID is a brand of Todo ID and the user ID is a brand of user ID and well this could be null in the case these errors basically so we can have a function and say print or get to do and then you can say ID you can say to do ID and then if you save result is different from null then get too and then result. toos at zero and then you can say ID and as you can see we get no error here but if we change this over to a user ID we get an error because the brand doesn't satisfy this brand type now just like s you can get access to the error and you can say error Dot and you get the cost the message the name G you get everything which is great and you get full type safety because as you can see when we're piping through this effect we know that it can result in a par error so we get full type safety here and this wraps up the video if you want to learn more about all of these Concepts and Advanced Techniques in typescript make sure to subscribe I'll see you in the next one
Info
Channel: Lucas Barake
Views: 2,887
Rating: undefined out of 5
Keywords:
Id: 52IlyMBN1eg
Channel Id: undefined
Length: 19min 24sec (1164 seconds)
Published: Sun May 26 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.