How to use TypeScript Enums and why not to, maybe

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what is up with typescript enums they're a little bit different than a lot of other typescript features and so in this video we're going to take a look at how they work and why maybe you don't want to use them so let's start with the basics an enum is a type that has a defined set of values that you can think of like tokens or symbols so i'm going to paste in here in our playground and example enum and this perhaps represents the state of a blog post or maybe a video in our content management system and as you can see we've got three states draft scheduled and published the actual values for these shouldn't really matter so much as long as they are unique and so now that we have the cnum we can actually use it so for example i can create something here that is of type post state and we can assign postdate.draft to that so notice a couple of things so first of all x has a type of post state and it has a value of poststate.draft so we're using post state as both a type and as a value or as a kind of holder for values and if you look at the compiled javascript over here on the right you can see that unlike a lot of types enums actually do have representations in javascript so you can see we're doing this kind of interesting function based thing where we have this post state object that we're initializing inside these square brackets we initialize poststate.draft to equal zero and then of course since that's an expression a return zero so we have post state at zero equals draft and so we're kind of mapping it both ways and so what we're seeing is by default when we have an enum like this typescript will actually set the values for our members here to be some incrementing number and we'll start at zero and we'll go up and we can actually see this in the editor too if i hover over them we can see what the values are if we hover over x here we can see that it is of type post state now if we remove this type i think we'll probably get a more narrow type we can see it's of type postdate.draft postdate.draft is both the type and the value for this particular variable which is kind of different from a lot of what we see in other parts of typescript where types and values are very separate and can't really be interchanged we can actually choose the values for these members if we want as you can see like i said we started with 0 1 and 2 but for example if we change draft here to be equal to 10 then what we're seeing and let me bump the font size up a bit for you uh what we're seeing here is that now we start at 10 and we still increment so we have 11 and 12 which makes sense right we can just increment like that or we could totally just choose values for all of these if we want if we set schedule to 20 we get 10 and 20 and then we start incrementing or we could choose a value for all three of these 10 20 and 30. now what's interesting about an enum like this and i'm going to go back to the automatically filled in types here so we have zero one and two again what's interesting about this is that it's a great way to represent a type that increments or goes up in value or even goes up in like scope so let me show you another example here of something that i actually have used in the past something pretty similar what we have is an enum that represents file permissions so we have none read write and execute right and so these are the four types of permissions you can have now let's say we're building a system where where these are not individual permissions but if you have right say then you must also have read and if you have execute you also have right and therefore read as well and so these are kind of like growing in scope not just in the actual value maybe that they represent setting this up in an enum makes it really easy to determine what someone can do with a file so for example we can have this can access file function where you can pass in the permission level that the user has for this file and then maybe the required permission for whatever operation they want to perform and we can return true if their permission is greater than or equal to the required permission right so we could see here that const t we can do can access file and we can do file perm read and that's the permission of the user and then we can say file perm dot maybe execute is what they need to be able to perform this operation and if we do console log for t and then i go over to the logs here and we actually run this you can see false because they need execute permission but if we change this to just read permission what we get is true because now this user can actually perform that operation on that file and it's a really nice easy way to handle those types of scope changes however this actually starts breaking down a little bit so we can have a function here that we can call get file and we're going to take a file permission p here okay so we have this function that takes a file permission now if i call get file i can pass it file permission dot read obviously right and that makes sense what i can also do is pass it the number that is represented by one of these so for example let's say instead of file permission.read i wanted to pass it execute which is three great so now we can see that this passes just fine as well even though this takes a file permission it will accept the number that represents it however this gets a little weird quickly so for example five uh oh five isn't actually a valid file permission however when an enum has numeric values like this it doesn't prevent you from just passing any number in to represent that now this is an intentional feature of typescript and i have an article on my blog that goes into a little more details about why so check that link out below the like button still it feels like something that's really easy to shoot yourself in the foot with right you can pass a value like this and we don't have that rigorous typing that you expect from the rest of typescript it feels like this is kind of varying from how most other pieces of typescript works so how can we get around this type of thing well there is something that we can do and that is we can actually use string values in our enum instead of numeric values so here's a new post-state enum we have a draft scheduled and published and this time instead of letting typescript determine the value or even assigning our own number values we are assigning string values here and when we assign these string values you can see our actual value representation is a little bit different in javascript instead of having that two-way mapping we just have post-state draft post-date scheduled and post-state published and those equal the strings that we said they should equal so this seems much nicer right there's no way that this could accept just random strings and in fact that's exactly true so if i create a variable x here that should be of type post state i can do post state dot draft and that's good and if i create another variable post state that i think should equal something else then what we can see is typescript complains that idea is not assignable to type post state that's great so it's keeping us within the scope of the values that we have or the members that we have in this enum however this is actually again not quite the way you would expect this to operate in typescript so let's create another variable here zed this should also be a post state and this time we will set it to the string draft again this doesn't work draft is not assignable to type post state this operates differently than the rest of typescript so as you know the rest of typescript operates with a kind of like shape or structure based typing where as long as the shape of your object matches the shape of the type that it needs to match typescript will consider it valid it doesn't actually have to be instantiated as that particular type however this works differently with these enums even though draft as a string accurately represents draft as one of the members of post state we can't actually use the raw string if you will as our value to a variable that has the type post state and we could say you know just don't do that always use the poststate.draft postdate.scheduled postdate.published because then you know you'll be safe but this gets tricky if you want to take input from a user right and you need to cast that to a particular enum member or maybe you're writing a typescript library and you have functions that require enum value as an argument your user will have to both use your library and also they'll have to import your enum as well they can't just use their own matching strings and have type safety in that way so using strings as your enum value is a little bit better than numbers but it still acts weird if you're familiar with a lot of the rest of the way typescript type mechanics work so for these reasons i generally try to avoid enums when writing typescript now you can use type unions instead and we had a video about this a couple of weeks ago i'll put a link to that up in the corner here but let me show you kind of how i do that so instead let's create this post state constant it's going to be a variable a value that of course will look almost the same in javascript a couple of things to notice here i've kind of named this the same way i named the enum so we've got post state draft scheduled and published with the same string values i'd put as const at the end here and this changes the type of this object so as you can see if i hover over post state here the type is essentially exactly the same as the value if we remove as const here you'll see that it doesn't quite look the same instead we have these keys and the values of those keys are just some string so it's not quite as narrow as we want when we put as const here typescript knows that these values can't actually change and so it will type them as strictly as possible so we have draft scheduled and published as the actual values of these keys in the type itself and that's key here so now this is the value that we can use and we can use this in the same way we were using before so i can say postdate dot draft and that works and now x here of course is draft now what i can't do though is use post state as the type if i try and put post state as the type here it will complain that i'm using a value as a type and maybe i meant to say type of post state well kind of let's copy that and we're going to use that to create a new type here so let's create a type called post state type unfortunately we can't use the same name post state because you know that's going to clobber the variable itself and be hard to reference but what we can do is just suffix it with type or maybe use some other convention that you have in your code base now let's see if we just say type post state that's not going to work right because the type of post state is the whole object and if we look at this x is supposed to be the whole object but instead it's a type string x needs to be one of the values of the keys of post state now we can say key of type of post state and this gets us a little bit closer if we hover over our type now we can see that it is draft scheduled and published however they're all in lower case which means they actually are the keys of our post state quote unquote enum they're not the values and we want the values that's pretty easy to do though we can wrap this in type of post state and then we'll use square brackets to index into that and this is our final type so we say get me the type of post state and we're going to index into that type using the key of the type of post state and so now if we hover over post state type we can see that it is a union of draft scheduled and published the actual values that were in our enum members before are now a union type that we can use to assign to that type and so now x can be of post state type because it is draft and now what this also means though we can use a raw string for this so we could say scheduled and notice we even get some autocomplete on that because post state type is just these three values we don't actually need to use poststate poststate.scheduled and said we can just use the raw string this is the type of thing i would do instead of using an enum often you could just write the union type directly so just pretty much write what i'm seeing in this hover over here something like that but if you want to do something a little more dynamic if you want to be able to have an object where you can reference the values of your enum like this then this is a great way to do it now i'm not saying never use enums there are legit cases for using a typescript enum my preference is to avoid them as much as possible because they act very differently than the rest of the types in my system but that doesn't mean you should avoid them as long as you understand some of the gotchas you'll be able to figure out what are the right use cases for enums in your application so that's the scoop on typescript enums like i said i have some more details about enums in a blog post so definitely check out that link in the description below thanks so much for watching and i'll see you next time the actual values
Info
Channel: Andrew Burgess
Views: 18,302
Rating: undefined out of 5
Keywords:
Id: pWPClHdcvVg
Channel Id: undefined
Length: 12min 43sec (763 seconds)
Published: Mon Aug 22 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.