In this lesson, we're going to learn how we can use const
assertions to implement an enum-style object in TypeScript. We generally don't want widening of any literal types in this object, so we can use a const
assertion. We're also going to compare our enum-style object to a native TypeScript enum.
Instructor: [0:00] In this example, I want to show you how we can use const assertions with enum-style objects. My fetchJSON function here accepts the HTTP method as a parameter. Down here in line 6, I am passing in the string "GET." Let's go ahead and refactor these HTTP methods into an enum-style object.
[0:20] I'm going to declare a variable called HTTPRequestMethod. Let's define a GET property and let's also define a POST property. We can now replace the string literal "GET" in our fetchJSON call by HTTPRequestMethod.GET.
[0:40] However, TypeScript is now giving us a type error, and it's saying that the argument of type string is not assignable to the parameter of either string literal type "GET" or string literal type "POST."
[0:53] We are getting this error because both the GET and the POST property are inferred to be of type string. They're not inferred to be the specific strings "GET" and "POST." We can add a type annotation to our enum-style object. We can tell TypeScript that we want the "GET" property to have the string literal type "GET", and the POST property to have the string literal type "POST."
[1:16] While we're at it, we might as well make both properties read-only. Our code now type-checks correctly and this is because the properties of our enum-style object now have the correct string literal types.
[1:30] If you look at the declaration of our enum-style object, you can see that we are repeating every HTTP method four times. Also, our type annotation is getting more verbose. Instead of using this type annotation, we can add a const assertion. With a const assertion, TypeScript will now infer the type that we just annotated ourselves.
[1:53] There is one more refactoring I want to make and that is to derive this union type from the values of our enum-style object. To make that a bit easier, I'm going to write a helper type called valuesOf and it's going to accept a generic type parameter T. What this type does is access the types of all properties in this object.
[2:15] I'm using the keyof operator here and what is called an indexed access type. Next, I'm going to create a type called HTTPRequestMethod type and it's going to use the values of helper type to extract all the property values from the HTTPRequestMethod enum.
[2:34] Note that HTTPRequestMethod is a value, not a type, so we have to use the typeof operator here. If I hover over the HTTPRequestMethod type, we can see that TypeScript has correctly extracted the string literal types "GET" and "POST." This means I can now replace this union type by our HTTPRequestMethod type.
[2:58] In this entire example, we've been using standard JavaScript syntax as well as TypeScript syntax. We have not been using TypeScript enums. JavaScript itself does not have enums, so we typically write these enum-style objects.
[3:11] In TypeScript, however, we could have also implemented HTTPRequestMethod as a TypeScript enum. The syntax for that is to say enum and then the name. Within the enum, we would have two members called GET and POST. Lastly, since enums are both a value and a type, we can refer to the enum name directly here.
[3:36] It's up to you whether you want to be using a TypeScript enum or an enum-style object. Some people like to avoid TypeScript enums because they are not currently a standard JavaScript feature. I find that this example is a little bit more readable using a TypeScript enum, but honestly, we could have also stuck with the enum-style object.