Recently, I was working on a large TypeScript project that required handling different user roles in a clean and maintainable manner. The requirements included different UI elements and access permissions based on user roles – admin, editor, viewer, etc. That’s when I realized how powerful TypeScript’s switch statements combined with enums can be.
In this tutorial, I’ll show you how to use switch statements with enums in TypeScript. Let’s dive in!
What are TypeScript Enums?
Enums in TypeScript provide a way to define a set of named constants. They make it easier to document intent or create a set of distinct cases. When combined with switch statements, they offer powerful compile-time safety.
Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent or create a set of distinct cases.
For example, here’s a simple enum that represents different user roles:
enum UserRole {
Admin,
Editor,
Viewer,
Guest
}
By default, enums are number-based, starting from 0. The above enum is equivalent to:
enum UserRole {
Admin = 0,
Editor = 1,
Viewer = 2,
Guest = 3
}
Using Switch Statements with Enums in TypeScript
Switch statements in TypeScript provide a clean way to handle different cases based on enum values. I will present you with various scenarios featuring real-life examples.
Pattern 1: Basic Enum Switch
Let’s start with a basic example. Imagine we’re building a system for an e-commerce platform in the US that handles different order statuses. Based on the order status, you can see how to use an enum in a switch statement in TypeScript.
enum OrderStatus {
Pending,
Processing,
Shipped,
Delivered,
Canceled
}
function getOrderStatusMessage(status: OrderStatus): string {
switch (status) {
case OrderStatus.Pending:
return "Your order has been received and is awaiting processing.";
case OrderStatus.Processing:
return "Your order is being processed and will ship soon.";
case OrderStatus.Shipped:
return "Your order has been shipped! Track your package with the provided tracking number.";
case OrderStatus.Delivered:
return "Your order has been delivered. Enjoy your purchase!";
case OrderStatus.Canceled:
return "Your order has been canceled. Contact customer support if you have questions.";
default:
return "Unknown order status";
}
}
// Usage
const message = getOrderStatusMessage(OrderStatus.Shipped);
console.log(message); // "Your order has been shipped! Track your package with the provided tracking number."
This approach works great, but what if we add a new status to our enum but forget to update our switch statement? TypeScript doesn’t automatically alert us to this potential issue. That’s where exhaustiveness checking comes in.
You can see the exact output in the screenshot below:

Check out Check If a Value Is in an Enum in TypeScript
Pattern 2: Exhaustiveness Checking
One of the most powerful patterns when using enums with switch statements is exhaustiveness checking. This ensures you’ve handled all possible enum values:
enum PaymentMethod {
CreditCard,
PayPal,
BankTransfer,
ApplePay,
GooglePay
}
function getPaymentProcessingFee(method: PaymentMethod): number {
switch (method) {
case PaymentMethod.CreditCard:
return 2.9; // 2.9% fee
case PaymentMethod.PayPal:
return 3.4; // 3.4% fee
case PaymentMethod.BankTransfer:
return 1.5; // 1.5% fee
case PaymentMethod.ApplePay:
return 2.0; // 2.0% fee
case PaymentMethod.GooglePay:
return 2.1; // 2.1% fee
default:
// This ensures we get a compile-time error if a new enum value is added
const _exhaustiveCheck: never = method;
throw new Error(`Unhandled payment method: ${_exhaustiveCheck}`);
}
}
const method = PaymentMethod.PayPal;
const fee = getPaymentProcessingFee(method);
console.log(`The processing fee for PayPal is ${fee}%`);
With this pattern, if we add a new payment method to our enum, TypeScript will throw a compilation error because our switch statement doesn’t handle the new case. This forces us to update our code accordingly.
The never type is key here – it ensures compile-time exhaustive checks for enums when they are used with switch/case statements.
You can see the exact output in the screenshot below:

Check out TypeScript Enum Reverse Mapping
Pattern 3: String Enums in Switch Statements
String enums add an extra layer of clarity to your code. Here’s how they work with switch statements:
enum USRegion {
Northeast = "NORTHEAST",
Southeast = "SOUTHEAST",
Midwest = "MIDWEST",
Southwest = "SOUTHWEST",
West = "WEST"
}
function getRegionalTaxRate(region: USRegion): number {
switch (region) {
case USRegion.Northeast:
return 6.25; // 6.25%
case USRegion.Southeast:
return 7.0; // 7.0%
case USRegion.Midwest:
return 5.75; // 5.75%
case USRegion.Southwest:
return 8.25; // 8.25%
case USRegion.West:
return 7.5; // 7.5%
default:
const _exhaustiveCheck: never = region;
throw new Error(`Unhandled region: ${_exhaustiveCheck}`);
}
}
// Example usage:
const region = USRegion.Southwest;
const taxRate = getRegionalTaxRate(region);
console.log(`The tax rate for ${region} is ${taxRate}%`);
String enums are particularly helpful when your enum values need to be meaningful strings, such as when they’re used in APIs or stored in databases.
Here is the exact output in the screenshot below:

Check out Convert TypeScript Enums to Arrays
Pattern 4: Using Enum Values in Switch Cases
Sometimes you need to use the actual enum values in your switch cases. Here’s how to do that:
enum HttpStatus {
OK = 200,
Created = 201,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
ServerError = 500
}
function handleHttpResponse(statusCode: number): string {
switch (statusCode) {
case HttpStatus.OK:
return "Request successful";
case HttpStatus.Created:
return "Resource created successfully";
case HttpStatus.BadRequest:
return "Invalid request parameters";
case HttpStatus.Unauthorized:
return "Authentication required";
case HttpStatus.NotFound:
return "Resource not found";
case HttpStatus.ServerError:
return "Server encountered an error";
default:
return `Unhandled status code: ${statusCode}`;
}
}
This approach allows you to use meaningful constants for HTTP status codes while still matching against the actual numeric values.
Check out Check If a String Is in an Enum in TypeScript
Advanced Patterns with Enum Switches
Let me share a more advanced pattern I’ve found useful in enterprise applications. This involves using enums with switch statements for dispatching actions:
enum UserAction {
Login,
Logout,
UpdateProfile,
ChangePassword,
DeleteAccount
}
interface ActionPayload<T = any> {
type: UserAction;
data?: T;
}
function userReducer(state: UserState, action: ActionPayload): UserState {
switch (action.type) {
case UserAction.Login:
const credentials = action.data as LoginCredentials;
return {
...state,
isLoggedIn: true,
username: credentials.username,
lastLogin: new Date()
};
case UserAction.Logout:
return {
...state,
isLoggedIn: false,
username: null,
lastLogin: null
};
case UserAction.UpdateProfile:
const profile = action.data as UserProfile;
return {
...state,
profile: {
...state.profile,
...profile
}
};
case UserAction.ChangePassword:
// Password change logic
return state;
case UserAction.DeleteAccount:
// Account deletion logic
return initialUserState;
default:
const _exhaustiveCheck: never = action.type;
return state;
}
}
This pattern is commonly used in state management systems like Redux, where having type safety for your actions is crucial.
Check out TypeScript Enum Naming Conventions
Combine Enums with Union Types
Another powerful pattern combines enums with union types for even more flexibility:
enum FoodCategory {
Appetizer,
MainCourse,
Dessert,
Beverage
}
interface MenuItem {
id: string;
name: string;
price: number;
category: FoodCategory;
// Additional properties based on category
spicyLevel?: 1 | 2 | 3; // For appetizers and main courses
containsAlcohol?: boolean; // For beverages
sugarLevel?: 'low' | 'medium' | 'high'; // For desserts
}
function validateMenuItem(item: MenuItem): boolean {
switch (item.category) {
case FoodCategory.Appetizer:
case FoodCategory.MainCourse:
return item.spicyLevel !== undefined;
case FoodCategory.Dessert:
return item.sugarLevel !== undefined;
case FoodCategory.Beverage:
return item.containsAlcohol !== undefined;
default:
const _exhaustiveCheck: never = item.category;
return false;
}
}
This pattern is especially useful when different enum values require different associated data or validation rules.
Read Convert String to Enum in TypeScript
Using ESLint for Exhaustiveness Checking
For improved developer experience, you can use ESLint rules to enforce exhaustiveness checks in your switch statements. This provides real-time feedback in your editor:
// Install the typescript-eslint plugin and configure the rule:
// "typescript-eslint/switch-exhaustiveness-check": "error"
enum Direction {
Up,
Down,
Left,
Right
}
function move(direction: Direction) {
switch (direction) {
case Direction.Up:
return { x: 0, y: 1 };
case Direction.Down:
return { x: 0, y: -1 };
case Direction.Left:
return { x: -1, y: 0 };
case Direction.Right:
return { x: 1, y: 0 };
// No default needed with the ESLint rule enabled
}
}
I hope you found this article helpful for understanding how to use switch statements with enums in TypeScript.
Using enums with switch statements allows you to write more robust, maintainable code by leveraging TypeScript’s type system. Remember to use exhaustiveness checking to ensure all cases are handled, especially as your enums evolve over time.
If you have any questions or suggestions, kindly leave them in the comments below.

Hey! I’m Bijay Kumar, founder of SPGuides.com and a Microsoft Business Applications MVP (Power Automate, Power Apps). I launched this site in 2020 because I truly enjoy working with SharePoint, Power Platform, and SharePoint Framework (SPFx), and wanted to share that passion through step-by-step tutorials, guides, and training videos. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.