🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)
🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)

🔧 Programmierung 🕛 kürzlich 8 Min Lesezeit
0

I Like Enums. My Teammate Preferred Literals. TypeScript Let Us Have Both.

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

I recently reviewed a contribution to a shared React module where one developer used const objects instead of enums for accepted prop values.



My first reaction was simple:




Why not enums?




I like enums for this kind of thing. Maybe too much.



For me, an enum is a clear and determined way to declare a list of accepted values. It says: here is the list, this is the source of truth, use these values everywhere.



For example:




CODE
enum ButtonVariant
{
Primary = "primary",
Secondary = "secondary",
Danger = "danger"
}






Then a component prop can be typed like this:




CODE
type ButtonProps =
{
variant: ButtonVariant;
};






And consumers use it like this:




CODE
<Button variant={ButtonVariant.Primary} />






I like that. It is explicit. It is easy to search. It is easy to rename. And if the actual value ever needs to change, I can change it in one place.



For example, if "primary" needs to become "main", I update the enum declaration:




CODE
enum ButtonVariant
{
Primary = "main",
Secondary = "secondary",
Danger = "danger"
}






The usages stay connected:




CODE
<Button variant={ButtonVariant.Primary} />






No walking through the whole codebase and manually replacing string literals.



So, naturally, I asked why the module used a const object instead.






The Const Object Approach



The approach looked something like this:




CODE
const ButtonVariant =
{
Primary: "primary",
Secondary: "secondary",
Danger: "danger"
} as const;

type ButtonVariant = (typeof ButtonVariant)[keyof typeof ButtonVariant];






This is a common TypeScript pattern.



The object stores the runtime values, and the type extracts a union of those values:




CODE
type ButtonVariant =
(
"primary"
|
"secondary"
|
"danger"
);






Then the prop can be typed like this:




CODE
type ButtonProps =
{
variant: ButtonVariant;
};






And consumers can pass literal values directly:




CODE
<Button variant="primary" />






The answer from the developer was fair.



In React components, literal values are often just nicer to use:




CODE
<Button variant="primary" />






No extra import.



Readable JSX.



Good IntelliSense.



Less ceremony for consumers of the component.



And honestly, I get it.



This is especially true in UI libraries. When someone writes JSX, they usually want a prop to feel lightweight. Importing an enum just to pass one prop can feel a bit heavy.



So there was a small design conflict:




CODE
<Button variant={ButtonVariant.Primary} />






vs.




CODE
<Button variant="primary" />









Why I Still Wanted Enums



I still wanted enums to be the source of truth.



The const object approach is practical, but for me enums still have a few important advantages.



First, enum is a built-in TypeScript language feature. It is not an emulation of enum-like behavior with an object. So if TypeScript improves enums in the future, enum-based code can naturally benefit from that. Objects are still just objects. It is a different paradigm.



Second, I do not see a strong optimization argument against enums here.



One of the points was that const object values are simple and can be optimized well by JavaScript engines.



That is true, but enum usage is not a real problem for this use case either. At runtime, a string enum access is still just a stable property access:




CODE
ButtonVariant.Primary






A const object is also an object with properties. So for a React prop value like this, I do not think this is the place where performance will be decided.



In practice, both approaches are simple enough here.



Third, objects can do more than just declare values. They can contain expressions, computed values, or even cause side effects during initialization.




CODE
const ButtonVariant =
{
Primary: getPrimaryVariant(),
Secondary: "secondary",
Danger: "danger"
} as const;






Maybe sometimes this is useful, but for declaring a fixed list of accepted values, I actually prefer the limitation of enums. With enums, TypeScript keeps the declaration much more strict and obvious.




CODE
enum ButtonVariant
{
Primary = "primary",
Secondary = "secondary",
Danger = "danger"
}






That is exactly what I want here: a clear list of allowed values.



But I also did not want to make the React component worse to use.



Because the other developer had a good point too:




CODE
<Button variant="primary" />






This is nice in JSX. No extra import. Easy to read. Autocomplete still helps.



So I started looking for a compromise:



Can we declare the accepted values as an enum, but allow consumers to pass either enum members or literal values?



Turns out, yes.






The Goal



Given this enum:




CODE
enum ButtonVariant
{
Primary = "primary",
Secondary = "secondary",
Danger = "danger"
}






I wanted both of these to be valid:




CODE
<Button variant={ButtonVariant.Primary} />
<Button variant="primary" />






But this should still be rejected:




CODE
<Button variant="random" />






So the prop type should accept:




CODE
ButtonVariant.Primary
|
ButtonVariant.Secondary
|
ButtonVariant.Danger






and also:




CODE
"primary"
|
"secondary"
|
"danger"






That became the idea behind SoftEnum.



I called it SoftEnum because the enum stays the source of truth, but the API is softer about what it accepts.






The Type



Here is the helper:




CODE
export type ExtractEnumValuesAsLiterals
<
T_Enum extends Record<string, string | number>,
T_Keys extends keyof T_Enum = keyof T_Enum
> =
(
T_Enum[T_Keys] extends `${infer T_Value}`
? T_Value
: never
);

export type SoftEnum
<
T_Enum extends Record<string, string | number>,
T_Keys extends keyof T_Enum = keyof T_Enum
> =
(
ExtractEnumValuesAsLiterals<T_Enum, T_Keys>
|
T_Enum[T_Keys]
);






Now the component can use the enum as the source of truth:




CODE
type ButtonProps =
{
variant: SoftEnum<typeof ButtonVariant>;
};






And both styles are accepted:




CODE
<Button variant={ButtonVariant.Primary} />
<Button variant="primary" />






But invalid values are still rejected:




CODE
<Button variant="random" /> // Type error









Why Not Just Use the Enum Type?



If the prop is typed directly as the enum:




CODE
type ButtonProps =
{
variant: ButtonVariant;
};






Then this is fine:




CODE
<Button variant={ButtonVariant.Primary} />






But this is not:




CODE
<Button variant="primary" />






Even though the runtime value of ButtonVariant.Primary is "primary", TypeScript still treats the enum member as its own enum member type.



That is useful in many cases, but here it makes the JSX API stricter than we want.






Why the Template Literal Inference Works



The interesting part is this:




CODE
T_Enum[T_Keys] extends `${infer T_Value}`
? T_Value
: never






This extracts the underlying literal value from the enum member type.



For string enums:




CODE
enum ButtonVariant
{
Primary = "primary",
Secondary = "secondary"
}

type ButtonVariantValues = ExtractEnumValuesAsLiterals<typeof ButtonVariant>;






The result is:




CODE
"primary"
|
"secondary"






Then SoftEnum combines that with the original enum member types:




CODE
type ButtonVariantInput = SoftEnum<typeof ButtonVariant>;






Conceptually, it becomes:




CODE
ButtonVariant.Primary
|
ButtonVariant.Secondary
|
"primary"
|
"secondary"






That means users can choose either style.






It Also Works With Numeric Enums



This helper is not only for string enums.



For example:




CODE
enum Status
{
Active,
Disabled
}






This works:




CODE
const status1: SoftEnum<typeof Status> = Status.Active; // valid

const status2: SoftEnum<typeof Status> = 0; // valid

const status3: SoftEnum<typeof Status> = "0"; // invalid






That last line is important.



The helper does not turn numeric enum values into strings. It extracts the numeric literal value.



So for numeric enums, SoftEnum<typeof Status> accepts:




CODE
Status.Active | Status.Disabled | 0 | 1






not:




CODE
"0" | "1"









Narrowing to Specific Enum Keys



The second generic parameter makes it possible to allow only part of an enum.




CODE
type PrimaryOrSecondaryOnly = SoftEnum
<
typeof ButtonVariant,
"Primary" | "Secondary"
>;






Now the accepted values are only:




CODE
ButtonVariant.Primary
|
ButtonVariant.Secondary
|
"primary"
|
"secondary"






This can be useful when one component supports only a subset of shared enum values.






The Compromise



This gave us a nice middle ground.



The library can still declare accepted values with enums:




CODE
enum ButtonVariant
{
Primary = "primary",
Secondary = "secondary",
Danger = "danger"
}






So the enum remains the source of truth.



But consumers can still write lightweight JSX:




CODE
<Button variant="primary" />






Or explicit enum-based code:




CODE
<Button variant={ButtonVariant.Primary} />






Both are type-safe.



Both are valid.



And invalid values are still rejected.






Final Thoughts



I still like enums.



They are a real TypeScript feature, not just an object pattern. They are explicit, searchable, refactor-friendly, and they give me one clear place where accepted values are declared.



But I also understand why literal values feel better in many React APIs. JSX is supposed to be easy to read and easy to write. Forcing imports for every simple prop value can make a component library feel heavier than it needs to be.



SoftEnum is a small type-level compromise:




CODE
export type SoftEnum
<
T_Enum extends Record<string, string | number>,
T_Keys extends keyof T_Enum = keyof T_Enum
> =
(
ExtractEnumValuesAsLiterals<T_Enum, T_Keys>
|
T_Enum[T_Keys]
);






It lets library authors keep enums as the source of truth, while component consumers can use simple literals when that feels more natural.



Not every API needs to pick one side forever.



Sometimes the nicest developer experience is just letting both styles coexist safely.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Hackers Just Poisoned the Rust Supply Chain | Threat Wire
1 Quelle
Hackers Found a Way Into Humanoid Robots | Threat Wire
1 Quelle
Bits und so #1021 (Passwort für Laufwerk)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten I Like Enums. My Teammate Preferred Literals. TypeScript Let Us Have Both.

Thematisch verwandte Begriffe: Like, Enums, Teammate, Preferred · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...