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

bi-enum

v1.0.1

Published

BiEnum 是一种静态枚举方案,是 TypeScript 原生枚举(enum)的加强版。BiEnum is a static enumeration scheme, which is an enhanced version of TypeScript's native enum.

Readme

BiEnum

简体中文 | English

1. Introduction

BiEnum is a web front-end static enumeration scheme, which is an enhanced version of TypeScript's native enum.

BiEnum strengthens the compile-time type checking of enumeration labels and values, and makes the code editor more accurate in IntelliSense.

BiEnum supports any value as an enumeration label or value, making it easy to design and implement heterogeneous enumerations or even nested enumerations.

2. Native enumeration

2.1 Loose type

TypeScript enum with enumeration labels and values of different JavaScript primitive types (such as string, number) are JavaScript that are bidirectional mapped between enumeration labels and values at runtime.

enum Direction {
    Up = 1,
    Down = 2,
}

Direction.Up // 1
Direction[2] // 'Down'

The native enum type check is not strict, the compiler only roughly determines that the enumeration label and value of Direction are string and number.

In the following case, neither 3 nor 4 is a legal enumeration value in business, but it can be compiled and passed, burying hidden dangers for the runtime stage.

let direction: Direction; // Actually, number
direction = 1; // ok
direction = true; // As expected: error, TS2322: Type 'true' is not assignable to type 'Direction'.
direction = 3; // Not as expected: compilation passed.

Direction[4]; // Not as expected: compilation passed.

TypeScript supports using values as types, so you might temporarily write as follows:

type DirectionValue = 1 | 2;
let direction2: DirectionValue = 3; // As expected: error, TS2322: Type '3' is not assignable to type 'DirectionValue'.

But this requires that every time you modify the definition of Direction, you also need to modify the definition of DirectionValue. Your brain needs to keep this in mind, or write it in a comment to remind the next developer not to forget. The risks involved are self-evident.

2.2 Restricted value range

It is impossible that enumerations in all business scenarios are simple mappings between strings and values. Static attributes that can be determined at the compilation stage can be enumerations in business and should also be implemented technically.

The following sample code will fail to compile:

enum BlueLikeColor {
    skyBlue = '#B2FFFF',
    royalBlue = '#002366',
}

enum Color {
    red = 0xff0000,
    green = 'rgb(0, 255, 0)',
    blueLike = [BlueLikeColor.skyBlue, BlueLikeColor.royalBlue], // error, Array is not allowed.
    unset = null, // error, TS2553: Computed values are not permitted in an enum with string valued members.
}

enum ColorDescription {
    [Color.red] = 'description about red', // error, TS1164: Computed property names are not allowed in enums.
    [Color.green] = 'description about red', // error, TS1164: Computed property names are not allowed in enums.
    [BlueLikeColor.skyBlue] = 1234, // error, TS1164: Computed property names are not allowed in enums.
}

Did you notice the compiler prompts? "Computed values are not permitted"。

We just said that TypeScript supports values as types. In theory, all literals and all attributes that can be determined at the compilation stage should and can be used as enumeration values.

If you have a certain understanding of TypeScript and listen to my analysis above, you may have some ideas.

It's too late, you will definitely not be anxious to look down at the design of BiEnum, but open the editor and write it yourself first.

3. BiEnum

3.1 Get started quickly

Install the bi-enum package:

npm i bi-enum

This time we use toBiEnum to create a bidirectional enumeration object:

import { toBiEnum } from "bi-enum";

// Create a BiEnum instance
const Direction = toBiEnum({
    Up: 1,
    Down: 2,
} as const); // Don't omit 'as const'

Write on the left side of the colon as the enumeration label, and write on the right side of the colon as the enumeration value. as const ensures that the types of 1 and 2 are not narrowed to number.

3.2 Precise type

Its prototype object provides two arrays, allLabels and allValues, whose values are also directly used as types.

// Value and type are both ['Up ',' Down ']
Direction.allLabels;

// Value and type are both [1, 2]
Direction.allValues;

// Equivalent to: type Direction = 1 | 2
type Direction = typeof Direction.allValues[number];

So now strict type checking will be performed at compile time:

let direction: Direction;
direction = 1; // ok
direction = true; // As expected: error, TS2322: Type 'true' is not assignable to type '2 | 1'.
direction = 3; // As expected: TS2322: Type '3' is not assignable to type '2 | 1'.

Direction[4]; // As expected: error, TS7053: ... Property '4' does not exist on type ... .

Again, all literals on the BiEnum construction parameter object will be directly used as types.

The BiEnum prototype object also provides two methods, isLabel and isValue, to determine whether the input parameter is a legal enumeration label or value.

let test: number = 1;
direction = test; // TS2322: Type 'number' is not assignable to type '2 | 1'.

if (Direction.isValue(test)) {
    direction = test; // ok, compilation passed.
}

3.3 Free value

Now migrate the previous Color case from enum to BiEnum:

  • ① Replace the equal sign between the enumeration label and value with a colon;
  • ② Replace enum with a call to toBiEnum;
  • ③ Add as const at the end of the constructor parameter object;
  • ④ In general, it is also necessary to define a type with the same name as the enumeration mapping object for the enumeration value range (allValues) (to comply with developers' usage habits of native enum)。
enum BlueLikeColor {
    skyBlue = '#B2FFFF',
    royalBlue = '#002366',
}

const Color = toBiEnum({
    red: 0xff0000,
    green: 'rgb(0, 255, 0)',
    blueLike: [BlueLikeColor.skyBlue, BlueLikeColor.royalBlue],
    unset: null,
} as const);
type Color = typeof Color.allValues[number];

const ColorDescription = toBiEnum({
    [Color.red]: 'description about red',
    [Color.green]: 'description about green',
    [BlueLikeColor.skyBlue]: 1234,
} as const);
type ColorDescription = typeof ColorDescription.allValues[number];

// Value and type are both 'description about red'.
ColorDescription[Color.red];

// Value and type are both 1234.
ColorDescription[BlueLikeColor.skyBlue];

The type system treats all literals as types, and the compiler does not report errors now.

4. Other instructions

4.1 Type restrictions on enumeration elements

BiEnum requires the enumeration label to be a classic primitive type (string | number | boolean | null | undefined).

There are no requirements for enumeration values, but if you set the value to a non-classic primitive type, BiEnum will only retain the one-way mapping from the enumeration label to the value.

The runtime BiEnum object does not preserve a reverse mapping where the enumeration value is a non-classic primitive types. You also won't see a reverse mapping of [object Object] to enumeration label in the editor's prompt list.

4.2 Enumeration elements hit keywords

If the business enumeration label or enumeration value has the same name as allLabels, allValues, isLabel, isValue, it will not affect the creation of bidirectional mapping objects, just get their references from the prototype.

const HitKeyword = toBiEnum({
    allLabels: 1,
    2: 'isLabel',
});
type HitKeywordLabel = typeof HitKeyword.allLabels[number]; 
type HitKeywordValue = typeof HitKeyword.allValues[number];

if (Reflect.getPrototypeOf(HitKeyword).isLabel(2)) {/* ... */}

for (const label of Reflect.getPrototypeOf(HitKeyword).allLabels) {/* ... */}