TypeScript Enums
Enums in TypeScript provide a way to define a set of named constants. They allow you to create a collection of related values that can be used as a type. Enums make your code more readable, maintainable, and less prone to errors by giving meaningful names to sets of numeric or string values.
Enum Basics
Here's a basic enum definition in TypeScript:
In this example:
- We define an enum called
Direction
with four values - By default, enum values are assigned incremental numeric values starting from 0
- You can use the enum as a type annotation (
let myDirection: Direction
) - You can access the enum's numeric value or name using different syntaxes
Numeric Enums
By default, enums in TypeScript are numeric. The first value is assigned 0, and subsequent values are incremented by 1:
Custom Numeric Values
You can assign custom numeric values to enum members:
When you assign a value to an enum member, subsequent members will be auto-incremented from that value:
Constant members are evaluated at compile time, while computed members are evaluated at runtime.
String Enums
TypeScript also supports string enums, where each member has a string value:
String enums have better readability and debugging experience compared to numeric enums because the values are meaningful when inspected at runtime. However, they don't support reverse mapping (you can't access Direction["NORTH"]
).
Const Enums
The const
keyword can be used with enums to improve performance:
At compilation, const enums are completely removed and their values are inlined wherever they're used. In the JavaScript output, you'll see:
This results in more efficient code but prevents certain runtime operations like reverse mapping.
Enum Member Types
Individual enum members can also serve as types. Let's look at an example:
In this example, we're using specific enum members (ShapeKind.Circle
and ShapeKind.Square
) as types for the kind
property, ensuring type safety.
Union Enums and Enum Member Types
Enums can be combined with union types to create powerful type constraints:
Enums at Runtime
Enums exist at runtime as real objects. This is different from TypeScript's other type constructs (like interfaces), which are erased during compilation:
This runtime presence allows for more dynamic operations with enums, but also increases the size of your generated JavaScript.
Practical Examples
Example 1: User Roles
Example 2: State Machine for Order Processing
This example demonstrates using enums to track the state of an order through its lifecycle.
Best Practices for Using Enums
1. Use PascalCase for enum names and enum members
2. Use string enums for better readability
3. Use const enums for better performance when possible
4. Consider alternatives to enums when appropriate
TypeScript offers other ways to represent a fixed set of values:
These alternatives can sometimes offer better type safety or smaller compiled code.
Exercises
Exercise 1: Weekdays
Description
In this exercise, you'll create a simple enum to represent the days of the week and use it to build a function that determines whether a day is a weekday or weekend.
Instructions
- Create an enum called
DaysOfWeek
with seven values:Monday
,Tuesday
,Wednesday
,Thursday
,Friday
,Saturday
, andSunday
. - Write a function called
isWeekend
that:- Takes a parameter of type DaysOfWeek
- Returns a boolean value: true if the day is a weekend (Saturday or Sunday), and false otherwise
- Test your function with different days of the week.
- Bonus: Create a function that returns the name of the next day given a current day.
Expected Output:
Exercise 2: API Response Status
Description
In this exercise, you'll create a string enum to represent different API response statuses and build a function that simulates API responses with different statuses.
Instructions
- Create a string enum called
ApiStatus
with at least four values:Success
,Error
,Loading
, andTimeout
. Assign appropriate string values to each. - Write a function called
simulateApiCall
that:- Takes a parameter
endpoint
of type string - Returns a Promise that resolves with an
ApiStatus
value - Simulates an API call by waiting for 1-2 seconds (using setTimeout)
- Randomly selects and returns one of the API statuses, with a higher probability for Success
- Takes a parameter
- Write another function called
handleApiResponse
that:- Takes an
ApiStatus
parameter - Logs different messages based on the status
- Takes an
- Test your functions by making several simulated API calls and handling the responses.
Expected Output:
Exercise 3: User Permissions System
Description
In this exercise, you'll create an enum-based permission system for a simple user management application. The system will define different permission levels and provide functions to check and manage user access.
Instructions
- Create an enum called
UserPermission
with the following values:None
Read
Write
Delete
Admin
- Create a
User
interface with:id
: stringname
: stringpermission
: UserPermission
- Create an array of sample users with different permission levels.
- Write the following functions:
canReadData(user: User): boolean
- returns true if user has Read permission or highercanWriteData(user: User): boolean
- returns true if user has Write permission or highercanDeleteData(user: User): boolean
- returns true if user has Delete permission or higherisAdmin(user: User): boolean
- returns true if user has Admin permissionpromoteUser(user: User): User
- returns a new user object with permission increased by one level (up to Admin)demoteUser(user: User): User
- returns a new user object with permission decreased by one level (down to None)
- Test your functions with the sample users to verify they work correctly.
Expected Output:
Summary
TypeScript enums provide a powerful way to define sets of named constants that improve code readability and maintainability:
- Numeric enums: The default, where members have numeric values
- String enums: Where members have string values for better readability
- Const enums: For better performance by inlining values at compile time
- Computed members: Dynamic values determined at runtime
Key best practices:
- Use PascalCase for enum names and members
- Prefer string enums for better debugging
- Use const enums for performance-critical code
- Consider alternatives like union types when appropriate
- Use powers of 2 for bit flags
Enums are a valuable tool in TypeScript's type system, helping you express intent more clearly and catch errors at compile time rather than runtime.