Why Check a Value in a TypeScript Enum? A Real-World Example
\n\nImagine: you are writing a backend in Node.js with TypeScript. A client sends an order status — the string "shipped". You have an enum OrderStatus with variants: Pending, Processing, Shipped, Delivered. But suddenly "CANCELLED" or "unknown" arrives. What to do? That's right — check if the value exists in the TypeScript enum.
This task is one of the most common on Stack Overflow (thousands of views). And for good reason: an enum in TypeScript is not just a set of constants, but a tricky beast with numeric and string variants. In this guide, we will break down all the ways to check for a value in an enum, from simple to advanced, with code examples and explanations.
\n\nWhether you are just starting to learn TypeScript or want to deepen your knowledge, this article is for you. Let's go!
\n\nWhat is an Enum in TypeScript? A Quick Primer
\n\nAn enum (enumeration) is a way to define a set of named constants. TypeScript has two types of enums:
\n\n- \n
- Numeric enums — values are automatically incremented (0, 1, 2...). \n
- String enums — each member is explicitly assigned a string. \n
Example of a numeric enum:
\n\nenum Direction {\n Up, // 0\n Down, // 1\n Left, // 2\n Right // 3\n}\n\nExample of a string enum:
\n\nenum Status {\n Active = "ACTIVE",\n Inactive = "INACTIVE",\n Pending = "PENDING"\n}\n\nWhy check a value at all? Because data from external sources (APIs, forms, databases) can be anything. Without a check, you risk getting undefined or a runtime error.
Method 1. The Classic: Object.values() + includes()
\n\nThe most popular and readable method is to turn the enum into an array of values and check for inclusion using includes().
For string enums:
\n\nenum Color {\n Red = "RED",\n Green = "GREEN",\n Blue = "BLUE"\n}\n\nfunction isColor(value: string): boolean {\n return Object.values(Color).includes(value as Color);\n}\n\nconsole.log(isColor("RED")); // true\nconsole.log(isColor("YELLOW")); // false\n\nFor numeric enums — a trick! Object.values() returns a reverse mapping (keys and values). So you need to filter only numbers:
enum NumberEnum {\n A = 1,\n B = 2,\n C = 3\n}\n\nfunction isNumberEnumValue(value: number): boolean {\n return Object.values(NumberEnum).filter(v => typeof v === 'number').includes(value);\n}\n\nconsole.log(isNumberEnumValue(2)); // true\nconsole.log(isNumberEnumValue(5)); // false\n\nThis approach is universal but requires caution with numeric enums. Pros: simple and clear. Cons: you need to remember about filtering.
\n\nMethod 2. Type Guard: Checking via Enum Keys
\n\nIf you need not just to check a value but also to get a type guarantee, use a type guard. This method is especially useful when working with APIs where you want to narrow the type.
\n\nenum UserRole {\n Admin = "admin",\n User = "user",\n Guest = "guest"\n}\n\nfunction isUserRole(value: string): value is UserRole {\n return Object.values(UserRole).includes(value as UserRole);\n}\n\nfunction processRole(role: string) {\n if (isUserRole(role)) {\n // TypeScript knows that role is UserRole\n console.log(`Role: ${role}`);\n } else {\n console.log("Unknown role");\n }\n}\n\nprocessRole("admin"); // Role: admin\nprocessRole("moderator"); // Unknown role\n\nThis approach provides type safety and reduces the number of checks in the code. I recommend it for serious projects.
\n\n