Question
TypeScript enums can use numeric values:
enum E {
Hello = 1,
World = 2,
}
E.Hello;
E.World;
How can I create an enum whose members have string values, such as "hello" and "world", and access them in the same way?
Short Answer
You will learn how to declare and use string enums in TypeScript. You will also see how string enums differ from numeric enums, how to validate string input safely, and when a string-literal union may be a simpler alternative.
Concept
An enum defines a fixed set of named choices. A string enum gives every choice a readable string value.
enum Greeting {
Hello = "hello",
World = "world",
}
Each member has two useful parts:
- The member name, such as
Greeting.Hello - The stored string value, such as
"hello"
const message: string = Greeting.Hello;
console.log(message); // "hello"
String enums are useful when a value is sent to an API, stored in a database, shown in logs, or placed in a URL. Unlike a numeric value such as 1, a value such as "pending" is understandable without looking up its meaning.
In current TypeScript, string enum members must be initialized with string literals or other constant enum members. They cannot be automatically generated in the way numeric enum members can.
Mental Model
Think of an enum as a labeled control panel.
Greeting.Hellois the labeled button you press in code."hello"is the value that button sends out.
The label prevents spelling variations throughout the program. Instead of repeatedly typing strings like "in-progress", you use one named, checked choice: Status.InProgress.
Syntax and Examples
Use enum, give each member a name, and assign a string value with =.
enum Status {
Pending = "pending",
Active = "active",
Completed = "completed",
}
const currentStatus: Status = Status.Active;
console.log(currentStatus); // "active"
console.log(Status.Completed); // "completed"
Status is both:
- A type that represents the allowed enum members.
- A runtime object that contains values such as
Status.Active.
A function can require one of the enum's values:
enum Role {
Admin = "admin",
Editor = "editor",
Viewer = "viewer",
}
function canDeletePosts(role: Role): boolean {
return role === Role.Admin;
}
canDeletePosts(Role.Admin); // true
canDeletePosts(Role.Viewer); // false
Use PascalCase names for enum types and member names by convention. The string values themselves are usually lowercase when they are API or storage identifiers.
Step by Step Execution
Consider this example:
enum DeliveryState {
Preparing = "preparing",
Shipped = "shipped",
Delivered = "delivered",
}
const state: DeliveryState = DeliveryState.Shipped;
if (state === DeliveryState.Shipped) {
console.log("Your package is on its way.");
}
Step by step:
DeliveryStatedeclares three named states.DeliveryState.Shippedevaluates to the string"shipped".- The variable
statestores that enum value. - The
ifcondition comparesstatewithDeliveryState.Shipped. - Both values are
"shipped", so the condition is true. - The message is printed.
Although the runtime value is a string, using DeliveryState.Shipped makes the intent clear and avoids repeating the raw string.
Real World Use Cases
String enums are especially useful when the value must remain readable outside TypeScript.
- API requests and responses: represent values such as
"admin","editor", and"viewer". - Order workflows: track
"pending","paid","shipped", and"cancelled". - Database fields: store a known category or state as a stable text value.
- Application configuration: select environments such as
"development","staging", and"production". - UI filters: represent sorting choices such as
"newest"or"price_low_to_high". - Logging and analytics: use readable event types such as
"user_signed_in".
Example of an API payload:
enum PaymentStatus {
Pending = "pending",
Paid = "paid",
Failed = "failed",
}
const payload = {
orderId: "ORD-1042",
paymentStatus: PaymentStatus.Paid,
};
// Sent as: { orderId: "ORD-1042", paymentStatus: "paid" }
Real Codebase Usage
In real projects, enums are often used at boundaries: places where your application communicates with users, APIs, databases, files, or configuration.
Prefer named members over raw strings
enum Environment {
Development = "development",
Production = "production",
}
function connect(environment: Environment) {
if (environment === Environment.Production) {
console.log("Using production settings");
}
}
Use guard clauses for invalid values
External input is not automatically safe merely because your internal code uses an enum. Validate values received from JSON, query parameters, or forms.
enum SortOrder {
Newest = "newest",
Oldest = "oldest",
}
function isSortOrder(value: string): value is SortOrder {
return Object.values(SortOrder).includes(value as SortOrder);
}
function getSortOrder(value: string): SortOrder {
if (!isSortOrder(value)) {
return SortOrder.Newest;
}
return value;
}
Use exhaustive switch statements
When handling every state, a switch makes each case visible.
enum TicketStatus {
Open = "open",
Closed = "closed",
}
function statusLabel(status: TicketStatus): string {
switch (status) {
case TicketStatus.Open:
return "Open";
case TicketStatus.Closed:
return "Closed";
}
}
Common Mistakes
Expecting automatic string values
Numeric enums can auto-increment, but string enums need explicit values.
// Invalid: string enum members need initializers
enum Color {
Red,
Blue,
}
Fix it by assigning every string value:
enum Color {
Red = "red",
Blue = "blue",
}
Using a raw string where an enum value is expected
enum Role {
Admin = "admin",
User = "user",
}
function setRole(role: Role) {}
setRole("admin"); // Avoid this; use the enum member.
Use:
setRole(Role.Admin);
This keeps call sites consistent and protects against typos.
Assuming string enums support reverse lookup
Numeric enums provide reverse mappings, but string enums do not.
enum Direction {
Up = "UP",
}
console.log(Direction.Up); // "UP"
// Direction["UP"] is not a reliable reverse lookup for a string enum.
If you need to test whether external input is valid, use Object.values(Direction).includes(...) and a type guard.
Confusing the member name with its stored value
enum Status {
InProgress = "in_progress",
}
console.log(Status.InProgress); // "in_progress", not "InProgress"
The member name is for TypeScript code. The assigned string is the runtime value.
Forgetting that untrusted input needs validation
A value from an HTTP request may be any string. Do not blindly assert it is an enum value with as Status; check it first.
Comparisons
| Feature | String enum | Numeric enum | String-literal union |
|---|---|---|---|
| Example | Status.Active = "active" | Status.Active = 0 | type Status = "active" | "inactive" |
| Runtime object exists | Yes | Yes | No |
| Readable in JSON and logs | Yes | Usually no | Yes |
| Auto-incrementing members | No | Yes | Not applicable |
| Reverse mapping | No | Yes | No |
| Best for | Stable named external values | Compact internal numeric categories | Lightweight type-only fixed strings |
A string-literal union is a common alternative when you only need type checking and do not need an enum object at runtime:
type Theme = "light" | "dark";
function setTheme(theme: Theme) {
console.log(theme);
}
setTheme("dark");
Use a string enum when named runtime constants such as Theme.Dark improve readability. Use a union when the direct strings are already simple and sufficient.
Cheat Sheet
// Declare a string enum
enum Status {
Pending = "pending",
Active = "active",
}
// Read a value
const value = Status.Pending; // "pending"
// Use it as a type
function update(status: Status) {}
update(Status.Active);
// Validate an external string
function isStatus(value: string): value is Status {
return Object.values(Status).includes(value as Status);
}
Rules:
- Assign an explicit value to every string enum member.
- Use
EnumName.MemberNameinstead of duplicating raw strings. - String enums do not have numeric enum reverse mapping.
- Validate strings received from outside the application.
- Choose stable string values if they will be stored or sent through an API.
FAQ
Can TypeScript enums have string values?
Yes. Assign a string literal to every member:
enum Size {
Small = "small",
Large = "large",
}
Why does a string enum member need an initializer?
TypeScript cannot automatically generate meaningful string values. Numeric members can increment from a number, but strings must be explicitly chosen.
How do I get the string value from a TypeScript enum?
Access the member:
enum Mode {
Dark = "dark",
}
const value = Mode.Dark; // "dark"
Can I convert a string to a string enum?
Only after validation. Check that the string exists in Object.values(MyEnum) before treating it as an enum value.
Do string enums have reverse mappings?
No. Unlike numeric enums, you cannot reliably look up a member name from the stored string using bracket notation.
Should I use a string enum or a union type?
Use a string enum when named runtime constants are helpful. Use a string-literal union when you only need a type and want less generated JavaScript.
Can enum values contain spaces or hyphens?
Yes, the assigned string can contain them:
enum SortOrder {
PriceLowToHigh = "price-low-to-high",
}
The enum member name must still be a valid TypeScript identifier unless written as a quoted property name.
Mini Project
Description
Build a small order-status utility for an online store. It uses a string enum so status values are readable in API payloads and consistently handled inside the application.
Goal
Create and validate order statuses, then return a user-friendly message for each valid status.
Requirements
Define an OrderStatus string enum with Pending, Paid, Shipped, and Cancelled values.
Create a function that checks whether an incoming string is a valid order status.
Create a function that returns a message for each order status.
Use a safe default message when an incoming status is invalid.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.