TypeScript enums aren’t the real problem — duplicated UI…
    Neura MarketNeura Market/CoPilot
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityCoPilotCoPilot
    DeepSeekDeepSeekStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsGamesBlogVideosGuidesCoursesCommunityPluginsTrending
    CoPilotBlogTypeScript enums aren’t the real problem — duplicated UI enum plumbing is
    Back to Blog
    TypeScript enums aren’t the real problem — duplicated UI enum plumbing is
    typescript

    TypeScript enums aren’t the real problem — duplicated UI enum plumbing is

    Sage Starr May 28, 2026
    0 views

    After enough frontend work, the “should we use TypeScript enums?” debate matters less than a more...

    After enough frontend work, the “should we use TypeScript enums?” debate matters less than a more practical problem.

    The enum itself is rarely the painful part.

    The painful part is keeping labels, colors, options, filters, and validation logic in sync.

    A status code starts as a simple backend value:

    • 0 = draft
    • 1 = published
    • 2 = archived

    Then the UI needs more:

    • a human-readable label,
    • a translated label,
    • a badge color,
    • a dropdown option list,
    • a table filter list,
    • maybe an icon,
    • maybe a helper to validate API values.

    And suddenly one tiny enum becomes three, four, or five runtime structures spread across your codebase.

    That’s the problem this post is really about.

    TL;DR

    • Native enums and as const objects are both useful.
    • Neither one gives you a built-in runtime source of truth for UI metadata.
    • enum-plus is most interesting when enum-like values need to drive labels, metadata, i18n, and UI lists from one definition.
    • If you only need constants and types, you probably don’t need it.

    The usual frontend enum mess

    A typical codebase ends up with something like this:

    export enum ArticleStatus {
      Draft = 0,
      Published = 1,
      Archived = 2,
    }
    
    export const articleStatusLabels: Record<ArticleStatus, string> = {
      [ArticleStatus.Draft]: 'Draft',
      [ArticleStatus.Published]: 'Published',
      [ArticleStatus.Archived]: 'Archived',
    };
    
    export const articleStatusColors: Record<ArticleStatus, string> = {
      [ArticleStatus.Draft]: 'gray',
      [ArticleStatus.Published]: 'green',
      [ArticleStatus.Archived]: 'red',
    };
    
    export const articleStatusOptions = [
      { value: ArticleStatus.Draft, label: articleStatusLabels[ArticleStatus.Draft] },
      { value: ArticleStatus.Published, label: articleStatusLabels[ArticleStatus.Published] },
      { value: ArticleStatus.Archived, label: articleStatusLabels[ArticleStatus.Archived] },
    ];
    

    None of this code is wrong.

    The problem is that one business concept now lives in multiple runtime structures, and they drift unless someone keeps them aligned.

    as const solves one problem — not all of them

    A plain as const object is still my default when I only need constants and a union type:

    const ArticleStatus = {
      Draft: 0,
      Published: 1,
      Archived: 2,
    } as const;
    
    type ArticleStatus = typeof ArticleStatus[keyof typeof ArticleStatus];
    

    That works well for type-level constraints.

    It stops being enough when the same values also need labels, i18n, colors, option lists, reverse lookups, or extra metadata at runtime.

    What enum-plus is actually good at

    enum-plus is easy to describe as “a drop-in replacement for native enum”, but I think that undersells the most useful part.

    enum-plus seems most useful when you want one runtime definition to drive both typed values and UI-facing data such as labels, metadata, and option lists.

    That means your enum can power:

    • application logic,
    • display labels,
    • localization,
    • dropdowns and menus,
    • table filters,
    • metadata lookups,
    • validation and lookup helpers.

    And it does this in a package that is:

    • zero dependency,
    • TypeScript-friendly,
    • usable in JavaScript too,
    • compatible with frontend frameworks and SSR.

    A more useful enum definition

    Here’s a practical example:

    import { Enum } from 'enum-plus';
    
    const ArticleStatus = Enum({
      Draft: {
        value: 0,
        label: 'Draft',
        color: 'gray',
        icon: 'edit',
      },
      Published: {
        value: 1,
        label: 'Published',
        color: 'green',
        icon: 'check-circle',
      },
      Archived: {
        value: 2,
        label: 'Archived',
        color: 'red',
        icon: 'archive',
      },
    });
    

    Now one definition can drive multiple use cases:

    ArticleStatus.Published;             // 1
    ArticleStatus.label(1);              // 'Published'
    ArticleStatus.key(1);                // 'Published'
    ArticleStatus.items;                 // UI-friendly iterable items
    ArticleStatus.findBy('color', 'red') // enum item lookup
    ArticleStatus.named.Published.raw;   // { value: 1, label: 'Published', color: 'green', icon: 'check-circle' }
    

    And if you want direct access to one metadata field:

    ArticleStatus.named.Published.raw.color; // 'green'
    

    That may not look dramatic at first glance, but it removes a lot of scattered glue code.

    Why this matters in real frontend code

    Suppose your backend returns article rows with a numeric status field.

    Your UI might need to:

    • render a label in a table,
    • render a badge color,
    • create filter options,
    • populate a form select,
    • localize the display text.

    With the usual approach, these concerns get split across separate maps and helpers.

    With a runtime enum definition, they can come from one place.

    That doesn’t just save lines of code.

    It reduces the number of places where your business vocabulary can silently diverge.

    Native enum vs as const vs enum-plus

    Here’s the practical tradeoff table I wish more articles included:

    ApproachGood for constants/typesLabels and metadata in the same definitionCan generate UI lists from the same definitionExtra maps/helpers needed
    native enumyesnonoyes
    as const objectyesnot by itselfnot by itselfyes
    enum-plusyesyesyesfewer

    So no, enum-plus is not the right answer for every project.

    But it is a strong answer for projects where enum-like values need to drive UI and business display behavior.

    What changed my mind about this category

    A lot of enum discussions in TypeScript are framed around language purity:

    • should we use enums at all?
    • should we prefer unions?
    • should we use as const objects instead?

    Those are valid questions.

    But in app development, the bigger problem is often operational duplication rather than syntax.

    If a single backend code needs to become:

    • a readable label,
    • a localized label,
    • a select option,
    • a filter option,
    • a badge color,
    • a searchable lookup,

    then the real design question becomes:

    Where does that information live?

    That’s the question enum-plus answers better than native enums do.

    When I would not use enum-plus

    This part matters.

    I would not use enum-plus if:

    1. I only need a few constants and a union type.
    2. I don’t need runtime labels or metadata.
    3. I’m writing a tiny module where plain objects are simpler.
    4. My team strongly prefers zero abstraction over convenience helpers.
    5. I don’t want another runtime dependency.
    6. My team already has a stable plain-object pattern that works well.
    7. My enum definitions are generated from API types and I don’t want a wrapper layer.

    In those cases, as const may be all you need.

    But if your codebase keeps rebuilding the same label maps, option arrays, and metadata dictionaries around status-like values, then it’s worth a serious look.

    Why this project seems worth watching

    A few things make the repo more credible than a random experiment:

    • active release history,
    • multiple contributors,
    • zero dependencies,
    • migration docs,
    • plugin system,
    • support for frontend-oriented use cases that many teams actually hit.

    That combination matters more to me than hype.

    Final thought

    I don’t think the strongest pitch for enum-plus is:

    “TypeScript enums, but better.”

    I think the stronger pitch is:

    “A single runtime source of truth for values your UI needs to display, translate, color, filter, and select.”

    I wouldn’t use this everywhere.

    But if your frontend repeatedly turns one enum into labels, colors, options, filters, and lookup helpers, then a runtime enum definition can be a reasonable abstraction.

    Repo: https://github.com/shijistar/enum-plus

    Tags

    typescriptjavascriptenumopensource

    Comments

    More Blog

    View all
    Five Gemma-4 models, one accelerator: what porting E2B 31B to AWS Inferentia2 taught megemma

    Five Gemma-4 models, one accelerator: what porting E2B 31B to AWS Inferentia2 taught me

    I ported the whole Gemma-4 family — E2B, E4B, 12B, 31B, and the 26B-A4B MoE — to run on...

    X
    xbill
    Hey DEV, I'm Tobore. Let's actually connect.community

    Hey DEV, I'm Tobore. Let's actually connect.

    Hey DEV, I'm Tobore. Let's actually connect. I've been on here for a while now, mostly writing and...

    L
    Laurina Ayarah
    I burned through thousands of AI tokens. Then a friend did it for freeai

    I burned through thousands of AI tokens. Then a friend did it for free

    (yep, kinda clickbait, just for the funsies 😊) At the beginning of the year, I relaunched my...

    P
    Paulo Henrique
    Claude might be saturating your machineai

    Claude might be saturating your machine

    My laptop was sitting idle with the fan at full tilt. Nothing was running that I knew of. The culprit...

    S
    Sidhant Panda
    Automated GitHub Code Reviews Using Google Geminigithubactions

    Automated GitHub Code Reviews Using Google Gemini

    I Built a Thing! TL;DR — Google Gemini-based Pull Request reviews and Issue Triaging for...

    D
    Darren "Dazbo" Lester
    What is an "agentic harness," actually?ai

    What is an "agentic harness," actually?

    I've been hearing the word "harness" thrown around a lot lately. I assumed it just meant "the IDE" or...

    T
    Tilde A. Thurium

    Stay up to date

    Get the latest CoPilot prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for CoPilot and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.

    Neura Market

    Custom AI Systems & Services

    Our team of experienced AI builders will help build custom AI systems, workflows, and solutions for your business.

    Request custom work

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this CoPilot resource

    • YouTube RAG Search with Frontend Using Apify, Qdrant, and AIn8n · $24.99 · Related topic
    • Provide Real-time Updates for Notion Databases via Webhooks with Supabasen8n · $14.99 · Related topic
    • Real-time Chat Translation with DeepLn8n · $4.99 · Related topic
    • Build a Self-Contained CRUD Application with Redis and HTML Frontendn8n · $14.99 · Related topic
    Browse all workflows