A TypeScript enum is a named set of constants. enum Direction { Up, Down, Left, Right } creates both a type, Direction, and a runtime object whose members you access as Direction.Up. Members are numbered from 0 unless you give them values, and string enums give each member a readable string.
Enums are one of the few TypeScript features that are not just types: an enum becomes a real JavaScript object when the code is compiled.
Numeric Enums
Without initializers, members get 0, 1, 2 and so on. Give the first member a number and the rest continue from it. You can also set every value explicitly, which is the safe choice when the numbers are stored in a database or sent over the network.
Relying on auto-numbering is fine for values that never leave the program. If the order of members might change, and the numbers are persisted anywhere, inserting a member in the middle silently renumbers everything after it.
What an Enum Compiles To
Types are erased, but an enum is not. This is the JavaScript TypeScript emits for a numeric and a string enum:
enum Direction { Up, Down, Left, Right }
enum Status { Active = "ACTIVE", Inactive = "INACTIVE" }
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
var Status;
(function (Status) {
Status["Active"] = "ACTIVE";
Status["Inactive"] = "INACTIVE";
})(Status || (Status = {}));
Direction["Up"] = 0 returns 0, so Direction[0] = "Up" is set in the same statement. A numeric enum therefore maps both ways: name to number and number back to name. That is the reverse mapping. String enums only map names to values.
The printed Direction object has eight keys: the four names and the four numbers. That matters as soon as you iterate over it.
String Enums
Each member of a string enum needs an explicit string value. The values show up as-is in logs, JSON and databases, which makes string enums easier to debug than numbers.
A string enum is nominal in one way that surprises people: a plain string is not assignable to it, even when the text matches a member's value.
index.ts(7,5): error TS2820: Type '"ACTIVE"' is not assignable to type 'Status'. Did you mean 'Status.Inactive'?
(The suggestion in the message is a guess by the compiler and is wrong here; the fix is Status.Active.) In the other direction, a Status value can be used wherever a string is expected. When values arrive as strings, from JSON or a form, convert them with a check like the one in the section on checking values below.
Using an Enum as a Type
The enum name is a type whose values are its members. Combined with switch, TypeScript checks that every member is handled when the function must return a value:
If a new member is added to Shape without a new case, sides stops compiling with TS2366, Function lacks ending return statement and return type does not include 'undefined'. The switch page shows the stricter never-based exhaustive check.
The last lines show a real weakness of numeric enums. A number literal that matches no member, const level: Level = 99, is a compile error (TS2322), but any value typed number is accepted, so 57 gets through. String enums do not have this hole.
Iterating Over an Enum
An enum is an object at runtime, so Object.keys, Object.values and Object.entries work. For a string enum they return exactly the members. For a numeric enum they also return the reverse-mapping entries, which you filter out:
To type a variable as "one of the enum's member names", use keyof typeof Direction, which is the union "Up" | "Down" | "Left" | "Right". Then Direction[name] looks the value up with full type safety.
A string enum has no reverse mapping, so to get a member's name from its value, search the entries: Object.entries(Status).find(([, v]) => v === "ACTIVE")?.[0] is "Active", or undefined when no member has that value.
Checking if a Value Is in an Enum
Data from outside the program is a plain string or number. A type guard checks it against the enum's values and narrows it to the enum type:
Avoid raw as Status on untrusted input: the assertion compiles, but nothing is checked at runtime, so "DELETED" would travel through the program typed as a valid Status.
const Enums
const enum asks the compiler to delete the enum and write each member's value where it is used. There is no object at runtime, so nothing can be iterated or reverse-mapped.
const enum saves a few bytes and a property lookup, but it depends on the compiler seeing the enum's declaration when it compiles every file that uses it. Tools that transpile one file at a time, such as Babel and swc, cannot see a const enum declared in another file; Node's type stripping rejects const enums like every other enum; and with isolatedModules or verbatimModuleSyntax TypeScript reports error TS2748 when you use a const enum from a declaration file. Most application code does not need const enums.
Enum vs Union Type vs as const Object
There are three common ways to define a fixed set of values:
enum | Union of literals | as const object | |
|---|---|---|---|
| Exists at runtime | yes, an object | no | yes, a plain object |
| Iterate the values | Object.values (numeric: filter) | no, nothing to iterate | Object.values |
Accepts a plain "red" | no (string enums) | yes | yes |
Named access X.Red | yes | no | yes |
| Reverse mapping | numeric enums only | no | no |
| Runs with Node type stripping | no | yes | yes |
Allowed by erasableSyntaxOnly | no | yes | yes |
| Extra syntax to learn | enum rules, const enums | none | the typeof pattern |
Many teams now default to a union of string literals, and switch to the as const object when they need the values at runtime (to iterate them or build a dropdown). The reasons: unions are pure types and disappear from the output; they accept the plain strings that JSON and APIs deliver; and enums are the one piece of everyday TypeScript that is not "JavaScript plus erasable types".
That last point has become practical. Node runs .ts files directly by stripping the types, and an enum is not something it can strip:
node status.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode
Node's --experimental-transform-types flag makes enums run, and the compiler option erasableSyntaxOnly reports every enum as error TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., so a project can forbid them up front. See running TypeScript for how type stripping works. None of this makes enums wrong: code compiled with tsc or a bundler runs them fine, and a codebase that already uses enums gains little from converting.
Frequently Asked Questions
What is an enum in TypeScript?
A named set of constants that is both a type and a runtime object: enum Direction { Up, Down } lets you write Direction.Up and use Direction as a parameter type. Unlike most TypeScript features, an enum is not erased: it compiles to a JavaScript object that exists at runtime.
How do I iterate over an enum in TypeScript?
For a string enum, Object.values(MyEnum) gives the values and Object.keys(MyEnum) the names. A numeric enum also contains reverse-mapping entries ("0": "Up"), so filter them out: Object.keys(Direction).filter((k) => isNaN(Number(k))) gives just the names. A const enum cannot be iterated, because it does not exist at runtime.
How do I convert a string to an enum value in TypeScript?
Check the string against the enum's values in a type guard: function isStatus(s: string): s is Status { return (Object.values(Status) as string[]).includes(s); }. After the check, s has type Status. A plain s as Status compiles but does no checking at runtime.
Should I use an enum or a union type in TypeScript?
Many teams prefer a union of string literals (type Status = "active" | "inactive"), or an as const object when they also need the values at runtime. Unions are erased completely, work with Node's built-in type stripping and the erasableSyntaxOnly option, and accept plain strings like "active". Enums are fine too, especially in codebases that already use them.
What is the difference between enum and const enum?
A regular enum compiles to an object you can iterate and look up at runtime. A const enum is removed during compilation and every use is replaced by its value (Size.Large becomes 2), so it costs nothing at runtime but cannot be iterated, and tools that compile one file at a time restrict it.