When to use Enums in Typescript
↳ Typescript
06 Apr 2020 | 6 minute read
Typescript gives great flexibility when defining interfaces and there are multiple ways of implementing the same thing. One such thing is working with constants in interfaces.
In my experience, this is one of the cases where enums
come in handy. They give us a way to reuse constants throughout our codebase which increases readability and reduces the need to repeat ourselves.
Let's say you're building an e-commerce platform, and that your application should handle both business-to-business (Professional) and business-to-consumer (Private) customers. As these customer segments should be treated somewhat differently in your application, you need to be able to identify which group a user belongs to.
As of this, you have the type
attribute in your Customer
interface.
interface Customer {
type: 'Private' | 'Professional';
firstName: string;
lastName: string;
email: string;
}
This gives us an interface that only accepts Private
or Professional
for the type
property.
If we try to have any other value for the type
property, Typescript will tell us that the expected type for the property type is invalid.
const customer: Customer = {
type: 'Jedi',
firstName: 'Oscar',
lastName: 'Alsing',
email: '[email protected]',
};
Type '"Jedi"' is not assignable to type '"Private" | "Professional"'
Earlier I mentioned that our application might want to treat Private and Professionals user differently. One such application might be to offer discounts to Professional users as they often buy in bulk. When calculating the price for your customer, you might implement a function that checks the type
of the customer, and displays a different price.
Note: Prices are usually calculated on the backend, and it's probably not a great idea to calculate the pricing tiers on the frontend in your production application.
const priceForCustomer = (defaultPrice: number, customer: Customer): number => {
switch (customer.type) {
case 'Private':
return defaultPrice;
case 'Professional':
return defaultPrice * 0.7;
default:
return defaultPrice;
}
};
Neat! But as our codebase grows, there will be more and more snippets in our code checking the type property of the customer to see whether the user is a Private
or Professional
user.
For some reason, we decide to change the naming conventions of the Customer
type
from Private
och Professional
to B2C
and B2B
.
But as we've used the Private
and Professional
throughout our application, we have no other choice but to change all of our Private
and Professional
. If your application is large, this could be non-trivial.
What we could have done is to use Enums. It would have enabled us to used named constants, which would have helped us when refactoring our application, as well as increasing readability of our code. Using enums, our interface could look like this:
enum CustomerType {
Private = 'Private',
Professional = 'Professional',
}
interface Customer {
type: CustomerType;
firstName: string;
lastName: string;
email: string;
}
In this example, I've used string enums instead of numeric enums, as it makes more sense in terms of what the data represents.
As type
property of our Customer
interface now accepts the CustomerType
enum, we now have two choices when working with the type of a Customer.
const priceForCustomer = (defaultPrice: number, customer: Customer): number => {
switch (customer.type) {
case 'Private': // Using the accepted values of the enum.
return defaultPrice;
case 'Professional':
return defaultPrice * 0.7;
default:
return defaultPrice;
}
};
Or using the CustomerType
enum:
const priceForCustomer = (defaultPrice: number, customer: Customer): number => {
switch (customer.type) {
case CustomerType.Private: // Using the enum named constants.
return defaultPrice;
case CustomerType.Professional:
return defaultPrice * 0.7;
default:
return defaultPrice;
}
};
If we then want to refactor from Professional
and Private
to B2B
and B2C
in our Customer
interface, all we need to do is to update the CustomerType
constant values.
enum CustomerType {
Private = 'B2C',
Professional = 'B2B',
}
Since we are now using CustomerType.Private
and CustomerType.Professional
instead of Private
and Professional
, in the priceForCustomer
function we don't have to do any other changes.
If you want to dig deeper into enums, the official documentation is great, and this article provides a lot of information as well.