To convert a string to an enum in TypeScript, check the string against the enum’s values with a type guard, then return the enum member or undefined. A plain as cast checks nothing:
const isStatus = (v: string): v is OrderStatus =>
(Object.values(OrderStatus) as string[]).includes(v);
My test setup for this post: TypeScript 7.0.2 and Node.js v22.22.2, strict mode on, and nothing in the outputs below is edited by hand.
You’ll see string enums, member-name lookups with keyof typeof, numeric enum traps, a generic helper, and how you handle input that doesn’t match.
Why can’t you assign a string to an enum in TypeScript?
Say order statuses arrive from an API as text. Even a matching string is refused:
enum OrderStatus {
Pending = "PENDING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED",
}
const fromApi: string = "SHIPPED";
const orderStatus: OrderStatus = fromApi;
Output:
enum_assign.ts(10,7): error TS2322: Type 'string' is not assignable to type 'OrderStatus'.

string could be anything, so TypeScript won’t treat it as an OrderStatus.Only enum members are allowed, and a string could hold "REFUNDED", so you must prove it’s valid first. New to enums? Start with my TypeScript enums guide.
Cast a string to an enum with as (no validation)
An assertion tells the compiler to trust you. Here’s what happens with a status the enum doesn’t have:
enum OrderStatus {
Pending = "PENDING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED",
}
const labels: Record<OrderStatus, string> = {
PENDING: "Waiting to ship", SHIPPED: "On the truck",
DELIVERED: "Delivered", CANCELLED: "Cancelled",
};
for (const raw of ["SHIPPED", "REFUNDED"]) {
const status = raw as OrderStatus; // compiles, checks nothing
const text: string = labels[status];
console.log(raw.padEnd(9), "->", text);
}
Output:
SHIPPED -> On the truck
REFUNDED -> undefined
"REFUNDED" passed the cast, and text is undefined although its type says string. So only cast after you’ve checked the value. The same hole bites switch statements on TypeScript enums.
Convert a string to a string enum with a type guard
The safe pattern is a type guard that checks Object.values(). When it returns true, TypeScript narrows the string to the enum type. Here it cleans up API data:
enum OrderStatus {
Pending = "PENDING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED",
}
const statusValues: string[] = Object.values(OrderStatus);
function isOrderStatus(value: string): value is OrderStatus {
return statusValues.includes(value);
}
function parseOrderStatus(value: string): OrderStatus | undefined {
const clean = value.trim().toUpperCase();
return isOrderStatus(clean) ? clean : undefined;
}
const orders = JSON.parse(`[
{ "id": 5011, "city": "Austin, TX", "total": 84.5, "status": "shipped" },
{ "id": 5012, "city": "Denver, CO", "total": 129.99, "status": "DELIVERED " },
{ "id": 5013, "city": "Boston, MA", "total": 42, "status": "refunded" }
]`) as { id: number; city: string; total: number; status: string }[];
for (const o of orders) {
const status = parseOrderStatus(o.status) ?? OrderStatus.Pending;
console.log(o.id, o.city.padEnd(11), `$${o.total.toFixed(2)}`.padStart(8), JSON.stringify(o.status).padEnd(13), "->", status);
}
Output:
5011 Austin, TX $84.50 "shipped" -> SHIPPED
5012 Denver, CO $129.99 "DELIVERED " -> DELIVERED
5013 Boston, MA $42.00 "refunded" -> PENDING

"refunded" falls back to PENDING.I widen the values to string[] once, so includes() accepts any string. The value is OrderStatus return type is what does the narrowing, and I explain it in the is keyword in TypeScript.
A default like Pending suits display code; for database writes, reject the record instead. For plain yes/no checks, see how to check if a string is in an enum.
Match the enum member name with keyof typeof
Sometimes your input holds the member name ("Shipped"), not the value ("SHIPPED"). Index the enum with keyof typeof after you check the key exists:
enum OrderStatus {
Pending = "PENDING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED",
}
type StatusKey = keyof typeof OrderStatus; // "Pending" | "Shipped" | "Delivered" | "Cancelled"
function statusFromKey(key: string): OrderStatus | undefined {
return Object.hasOwn(OrderStatus, key) ? OrderStatus[key as StatusKey] : undefined;
}
console.log(statusFromKey("Shipped"));
console.log(statusFromKey("SHIPPED"));
console.log(statusFromKey("toString"));
Output:
SHIPPED
undefined
undefined
Object.hasOwn() skips inherited names like toString, and lookups are case sensitive. My keyof operator guide explains the typeof part; the reverse is in getting an enum key by value.
Convert a string to a numeric enum
Numeric enums also map each number back to its name at runtime. That reverse mapping creates three traps:
enum Priority {
Low = 1,
Medium,
High,
}
console.log("Object.values:", Object.values(Priority));
console.log('Priority["High"]:', Priority["High" as keyof typeof Priority]);
console.log('Priority["2"]: ', Priority["2" as keyof typeof Priority]);
console.log('"toString" in Priority:', "toString" in Priority);
function parsePriority(input: string): Priority | undefined {
const key = input.trim();
if (/^\d+$/.test(key)) {
const n = Number(key);
return Priority[n] !== undefined ? (n as Priority) : undefined;
}
const value = Object.hasOwn(Priority, key) ? Priority[key as keyof typeof Priority] : undefined;
return typeof value === "number" ? value : undefined;
}
for (const s of ["High", "2", "9", "toString", "high"]) {
console.log(`parsePriority(${JSON.stringify(s)}) =`, parsePriority(s));
}
Output:
Object.values: [ 'Low', 'Medium', 'High', 1, 2, 3 ]
Priority["High"]: 3
Priority["2"]: Medium
"toString" in Priority: true
parsePriority("High") = 3
parsePriority("2") = 2
parsePriority("9") = undefined
parsePriority("toString") = undefined
parsePriority("high") = undefined

parsePriority().Object.values(Priority)includes the names, so a value check would accept"High"as a value.Priority["2"]gives"Medium", a string, because of the reverse mapping."toString" in Priorityistrue, so theinoperator isn’t a safe key check.
parsePriority() handles both forms: digits go through the number check, and names must map to a number. The mapping itself is explained in reverse mapping in TypeScript enums.
A generic helper to convert any string to an enum
With several string enums, write the check once. This generic returns the correct enum type for whichever enum you pass in:
enum OrderStatus {
Pending = "PENDING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED",
}
enum Region {
West = "WEST",
Midwest = "MIDWEST",
South = "SOUTH",
Northeast = "NORTHEAST",
}
function toEnum<E extends Record<string, string>>(enumObj: E, value: string): E[keyof E] | undefined {
const values: string[] = Object.values(enumObj);
return values.includes(value) ? (value as E[keyof E]) : undefined;
}
const s: OrderStatus | undefined = toEnum(OrderStatus, "DELIVERED");
const r: Region | undefined = toEnum(Region, "MIDWEST");
const bad = toEnum(Region, "Pacific");
console.log(s, r, bad ?? "rejected: Pacific");
Output:
DELIVERED MIDWEST rejected: Pacific

"Pacific" is rejected.Record<string, string> keeps numeric enums out, since reverse mapping would fool the check. To list valid options in an error, see getting all enum values in TypeScript.
Which string to enum method should you use?
| Method | Validates? | Use it when |
|---|---|---|
value as MyEnum | No | The value was already checked |
Type guard on Object.values() | Yes | Input is the enum value, like "SHIPPED" |
Object.hasOwn + keyof typeof | Yes | Input is the member name, like "Shipped" |
Generic toEnum() | Yes | Several string enums in one codebase |
Tired of converters? A string literal union may fit better; I compare both in enums vs string literal unions. Also see enums with multiple values in TypeScript.
Here are more enum tutorials worth a look:
- Convert a TypeScript enum to a string
- Check if a value exists in an enum
- Compare enum values in TypeScript
- Convert a string to a number in TypeScript
Frequently asked questions
How do I convert a string to an enum in TypeScript?
Check it with a type guard such as Object.values(MyEnum).includes(value), then return the narrowed value, or undefined when it doesn’t match.
Why does value as MyEnum not catch invalid strings?
A type assertion only changes the compile-time type. Nothing runs at runtime, so "REFUNDED" as OrderStatus stays the string "REFUNDED".
How do I convert an enum name like “Shipped” to its value?
Check Object.hasOwn(MyEnum, key), then read MyEnum[key as keyof typeof MyEnum].
How do I make the conversion case insensitive?
Normalize first, for example value.trim().toUpperCase() when every enum value is uppercase, then run the type guard.
Why does Object.values() show names for a numeric enum?
Numeric enums also store number-to-name entries (reverse mapping), so Object.values() returns both names and numbers.
Where are TypeScript enums documented?

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn