If you’re looking to make your TypeScript code more organized, enums are a powerful tool. They group related values together, giving your code better structure and readability. Let’s dive in and explore how to use them!
In TypeScript, enums are declared using the enum
keyword. For example, you could create an enum to represent the different fruit prices as follows:
Example 1: Basic Enum Structure — Key Movements
Demonstrates a common use case for enums: making choices within a limited set clearer.
enum Movement {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
function handlePlayerInput(key: string) {
switch (key) {
case Movement.Up:
// Move player character up
break;
case Movement.Down:
// Move player character down
break;
// ... other cases
}
}
This demonstrates the fundamental syntax for defining an enum. The Movement
enum has four members representing directions.
Example 2: Enums with Values
Enums can have associated values (numbers, strings, etc.). Here, StatusCode
associates HTTP status codes, ensuring type-safety and preventing the use of arbitrary numbers.
enum StatusCode {
OK = 200,
BadRequest = 400,
NotFound = 404
}
function handleResponse(code: StatusCode) {
if (code === StatusCode.OK) {
// Handle successful response
} else if (code === StatusCode.NotFound) {
// Handle resource not found
}
// ... other cases
}
Image source: https://restfulapi.net/http-status-codes
Example 3: Enum from Redux Toolkit
Redux Toolkit is a popular library for state management in React applications. It makes heavy use of TypeScript for type safety.
This enum defines distinct states for asynchronous actions (like fetching data). This is common in state management libraries.
enum PayloadActionLoadingState {
Idle = "idle",
Loading = "loading",
Failed = "failed",
Success = "success"
}
Example 4: Enum as a Discriminated Union
This enum defines two possible shapes: Circle
and Rectangle
. It acts as a foundation for ensuring type safety when working with different shapes.
Each shape type (Circle
, Rectangle
) is represented as a member of the ShapeType
enum.
The Shape
interface has a type
property that must be a member of the ShapeType
enum.
enum ShapeType {
Circle = "Circle",
Rectangle = "Rectangle"
}
interface Shape {
type: ShapeType;
}
interface Circle extends Shape {
type: ShapeType.Circle;
radius: number;
}
interface Rectangle extends Shape {
type: ShapeType.Rectangle;
width: number;
height: number;
}
function calculateArea(shape: Shape): number {
switch (shape.type) {
case ShapeType.Circle:
const circle = shape as Circle; // Type assertion to Circle
return Math.PI * circle.radius * circle.radius;
case ShapeType.Rectangle:
const rectangle = shape as Rectangle; // Type assertion to Rectangle
return rectangle.width * rectangle.height;
default:
throw new Error("Invalid shape type");
}
}
Specific shape interfaces (Circle
, Rectangle
) extend the base Shape
interface and must have their type
property set to the corresponding enum value.
This lets the calculateArea
function use the type
property as a discriminator to determine the appropriate calculation.
Example 5: Enums as Data Structures
This TypeScript code defines a simple model for representing playing cards, focusing on the suits, ranks, and the color of the card, which is derived from its suit.
It consists of two enums, a function to get a card’s numerical value, an interface to describe a card’s structure, and a function to create a card.
enum Suit {
Hearts = "♥", // Red suit
Diamonds = "♦", // Red suit
Clubs = "♣", // Black suit
Spades = "♠" // Black suit
}
enum Rank {
Ace = 1,
Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
Jack, Queen, King
}
function getCardValue(rank: Rank): number {
if (rank <= Rank.Ten) {
return rank;
} else {
return 10;
}
}
interface Card {
suit: Suit;
rank: Rank;
color: string; // Derived property based on suit
}
function createCard(suit: Suit, rank: Rank): Card {
return {
suit,
rank,
color: suit === Suit.Hearts || suit === Suit.Diamonds ? 'Red' : 'Black'
}
}
// Usage
let card1 = createCard(Suit.Hearts, Rank.Ace);
console.log(`The Ace of Hearts is red: ${card1.color}`); // Output: The Ace of Hearts is red: Red
let card2 = createCard(Suit.Spades, Rank.Queen);
console.log(`The Queen of Spades is black: ${card2.color}`); // Output: The Queen of Spades is black: Black
Function getCardValue
takes a Rank
as an argument and returns a number. For ranks Ace
through Ten
(numerically 1 to 10), it returns the rank's numeric value. For face cards (Jack
, Queen
, King
), which have rank values greater than 10, it returns 10.
Two card objects are created using the createCard
function: card1
as the Ace of Hearts (which is red) and card2
as the Queen of Spades (which is black).
This code is a straightforward way to model the basic properties of playing cards in a type-safe manner using TypeScript’s features like enums, interfaces, and type inference.
Conclusion
Enums provide a solid foundation for clean and structured TypeScript code. As your projects grow in complexity, their benefits will continue to shine.
TypeScript enums are a powerful tool to elevate your code. They enhance readability, maintainability, and type safety.
Check out my other TypeScript articles:
- TypeScript Index Signatures: 4 Examples Type-Safe Dynamic Objects
- Making React Components More Flexible with TypeScript Generics: 3 Examples
- 5 Resources Each TypeScript Developer Should Know About
This article was originally posted on Medium.
Top comments (9)
The initializers here are not necessary. We could leave off the initializers entirely.
But of course, you can still use String enums
I prefer using regular objects with "as const" to act like enums as it has more use cases
Great advice, thanks!
Object like
can work the same way as enums.
Could you mention the use cases where this approach is better?
When working with Angular, if you define an
enum
to be used as aninput
property, it willl require you to assign thatenum
to a class attribute of the parent component and later use it in thehtml
template, else you simply can't us it. An this is just an example.In that case, a map using
as const
and alsokeyof typeof
will let you to use the typed string in thehtml
.The approach that unify the definition for
enum
and let you use it as is, is the following:The real problem is that TypeScript doesn't recognize an
enum
as astring
, even if theenum
values are all strings. This aspect ofenum
make them "weird" in constrast ofas const
objects.A better explanation: dev.to/muszynov/something-about-ty...
You can change it to:
const enum loadingState
and it will work the same without being smarter givingas const
at the end.Forgive my ignorance about enums 🥺, but what would be a real use case instead of Object?
I really like that in your examples the enumeration are named : which makes it so much better to log them (otherwise you get numbers) ☺️
Thank you!
Hope you learned something new from the article 😊
I like these examples. it's easy to catch and memorize.