Quick Overview
A numeric `enum` emits a real object and a reverse mapping into the bundle. `as const` produces the same literal union with zero runtime output, which is why it is now the usual choice.
In software engineering, you often need to group named constant values together (such as HTTP status codes, user roles, or payment states).
TypeScript provides the enum keyword for defining named constants:
// String Enum
enum UserRole {
Admin = "ADMIN",
Editor = "EDITOR",
Viewer = "VIEWER",
}
function checkAccess(role: UserRole) {
if (role === UserRole.Admin) {
console.log("Full access granted.");
}
}
as const (Const Assertions)While Enums generate extra JavaScript code during compilation, Const Assertions (as const) create zero-overhead runtime objects with narrow literal types:
// Plain JavaScript object with 'as const' assertion
const USER_ROLES = {
Admin: "ADMIN",
Editor: "EDITOR",
Viewer: "VIEWER",
} as const;
// Extract union type from the object values
type Role = typeof USER_ROLES[keyof typeof USER_ROLES]; // "ADMIN" | "EDITOR" | "VIEWER"
function setRole(r: Role) {
console.log("Setting role to:", r);
}
setRole(USER_ROLES.Admin); // Valid!
| Feature | Enum | Const Assertion (as const) |
|---|---|---|
| Runtime JS Output | Generates IIFE object code | Generates plain JS object |
| Type Narrowing | Nominal Enum type | Literal Union type |
| Tree-Shaking | Harder for bundlers | 100% Tree-shakable |
enum Status { Pending, Active }) generate reverse mapping objects at runtime, making them accept arbitrary numbers without type warnings! Default to string enums or as const.Use as const for clean, tree-shakable constants. Next, let's explore Literal Types and Template Literal Types!
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.