When a Value Isnât Just a Value
Enums in TypeScript turn raw values into named concepts, helping you move from loosely defined data to clearly expressed meaning within your system.
Thereâs a subtle shift that happens when you move from JavaScript to TypeScript.
At first, it feels like youâre just adding types.
But slowly, you start realizing youâre not just working with data anymore.
Youâre working with meaning.
And enums are one of the clearest examples of that shift.
The Problem With âJust a Stringâ
In JavaScript, itâs very common to represent states like this:
let status = "success"It works. Itâs simple.
But it comes with a quiet problem.
Nothing stops you from doing this:
status = "succes" // typo
status = "done" // unexpected valueThe issue isnât just correctness.
Itâs that the value "success" has no protection, no structure, and no shared agreement.
Itâs just⌠a string.
When Values Start Carrying Meaning
At some point, you realize:
Not all values are equal.
Some values represent states, roles, or decisions.
Theyâre not just data â they carry meaning inside your system.
And once something carries meaning, you donât want it to be loosely defined anymore.
Introducing a Named Set
This is where enums come in.
enum Status {
Success = "success",
Error = "error"
}At first glance, it looks like a simple mapping.
But something important just happened.
You moved from:
"success"to:
Status.SuccessAnd thatâs not just a different syntax.
Itâs a different way of thinking.
From Raw Values to Named Concepts
Before enums, you think in raw values:
"success"
"error"After enums, you think in named concepts:
Status.Success
Status.ErrorThat small change creates a boundary.
Youâre no longer passing around arbitrary strings.
Youâre passing around meaningful states.
Why This Matters More Than It Seems
Consider this:
function handle(status: Status) {
if (status === "success") {
console.log("ok")
}
}This works.
But something feels off.
You defined an enum, but youâre still comparing with a raw string.
Which means:
You stepped outside the abstraction you just created.
The enum was supposed to define a controlled set of meanings.
But by comparing with "success", youâre back to treating it as plain data.
Staying Inside the Abstraction
The intended way is:
if (status === Status.Success) {
console.log("ok")
}Now everything stays consistent.
Youâre no longer relying on raw values.
Youâre relying on the contract of the enum.
What You Gain From This
This shift gives you a few important things:
- consistency â one source of truth
- safety â no typos or unexpected values
- refactoring â change once, update everywhere
- readability â code expresses intent, not just data
But the real gain is deeper.
A Layer of Meaning
Enums introduce a layer between:
Raw data â System logicInstead of your system depending on:
"success"It depends on:
Status.SuccessWhich means:
Your logic is now tied to meaning, not representation.
The Hidden Trade-off
But enums are not always the perfect solution.
They generate runtime code.
They are slightly heavier than simple type unions.
And sometimes, a simpler approach works just as well:
type Status = "success" | "error"So the question becomes:
Do I need a runtime object, or just a compile-time constraint?
A Small Shift in Thinking
Enums are not about replacing strings.
Theyâre about changing how you relate to them.
Instead of asking:
âWhat value is this?â
You start asking:
âWhat does this represent?â
And once you see that difference, enums stop being just another feature.
They become a way to make your code speak more clearly â not just to the compiler, but to yourself.