npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

unique-enum

v0.1.5

Published

Runtime-safe alternative to typescript enums that maintains type safety and ergonomics

Readme

unique-enum

A runtime-safe alternative to TypeScript enums that maintains type safety and ergonomics.

import { Enum } from 'unique-enum';      // ESM
const { Enum } = require('unique-enum'); // CommonJS

const Direction = Enum(
    'North',
    'East',
    'South',
    'West'
);

Installation

You can install unique-enum from npm:

npm install unique-enum

Usage

An enum can be declared as in the first example. The Enum function returns a class with its members as its only possible instances. Its members will autocomplete like normal enum variants (eg. typing Direction. will show North, East etc. after declaration.)

Each variant's string representation can be accessed using the variant property or by calling toString:

console.log(Direction.North.variant) // prints: 'North'

Enums also have convenient string representations and will work normally with Object.keys, iterators etc:

console.log(Direction.toString()); 
// prints: Enum { North, East, South, West }
console.log(Object.keys(Direction)); 
// prints: [ 'North', 'East', 'South', 'West' ]

for(let direction of Direction) { /* iterates over the variants */ }

Type Invariants

A unique enum guarantees the following invariants throughout the program:

  • Each variant will only be equal to itself and no other object or primitive.
  • Variants are the sole objects for which instanceof enum will be true.
  • After declaration, no new variants can be constructed or assigned, and variants cannot be changed or destroyed.
new Direction('northwest');       // Error
Direction.NorthWest = {};         // Error
Direction.North = 0;              // Error
Direction.North.variant = 'South' // Error
delete Direction.North;           // Error

function is_north(d) {
    return ( 
        d instanceof Direction    // Only true for the 4 Direction instances
        && d == Direction.North   // Only true for Direction.North
    );
}

These invariants make unique enums more resilient at runtime and when used from vanilla JavaScript.

Debugging enum values

Since unique enums maintain type information at runtime, inspecting them makes debugging easier compared to TypeScript's implementation.

console.log(Direction.North) // prints: Value { variant: 'North' }

Unique Enums + TypeScript

If you want to use variants of a specific enum as an object property or function argument, you can use the provided Variant type.

import { Variant } from 'unique-enum';

function is_north(d: Variant<typeof Direction>): d is typeof Direction.North { 
    return d == Direction.North; 
}

is_north(Direction.North);     // true
is_north(Direction.South);     // false
isNorth({ variant: 'North' }); // Error

Unfortunately, unlike TypeScript enums, unique enums cannot autofill switch statements at runtime, and hacks like a default arm that returns never will not work to check for exhaustiveness. This is because the type checker is unable to check for exhaustiveness of types that are not unions of literal types.

Limitation of TypeScript's Type System

TypeScript's type system has a limitation when dealing with unique enum variants: For two enums E1 and E2, a variant of E1 is considered compatible with a variant of E2 if their names are the same, and the variant names of E1 form a subset of those in E2.

const SoftwareVersion = Enum("V1", "V4", "V6");
const IP = Enum("V4", "V6"); 

let incompatible: typeof SoftwareVersion.V4 = IP.V4; // Assignment is allowed despite the types being incompatible

Note that the assignment will instead fail if IP contains any variant name that is not also a variant name of SoftwareVersion, so this should not be a problem in practice.

This flaw is due to the type system's inability to distinguish between different instances of classes returned from a function even if they have non-overlapping private members, an issue which is considered a design limitation.

However, note that TypeScript enums are themselves not fully type safe. While they do not suffer from this specific flaw, enum variant types are considered equivalent to their literal types, so the following is valid code:

enum IP { V4, V6 }
let literal: IP.V4 = 0; // No error even though 0 is not an IP