@andrewdonelson/byte-enum
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

ByteEnum Logo

ByteEnum

A high-performance, memory-optimized enum implementation for TypeScript with robust utility methods.

npm version License: MIT Test Coverage

Table of Contents

Overview

ByteEnum provides an alternative to TypeScript's built-in enums, focusing on memory efficiency while maintaining type safety and adding powerful runtime utility methods. It stores enum values as single bytes (numbers 0-255) or as single characters, dramatically reducing memory usage when storing large collections of enum values.

The implementation uses optimized data structures (Map and pre-computed arrays) to ensure lookup performance is comparable to native TypeScript enums.

Features

  • Memory Efficiency: Uses single bytes or characters instead of full strings
  • Type Safety: Full TypeScript type checking and inference
  • Fast Lookups: O(1) performance for value-to-key lookups
  • Key Normalization: All keys are normalized to UPPERCASE for consistency
  • Case Insensitivity: Lookups work regardless of case
  • Formatting Options: Convert enum values to human-readable formats
  • Extensive Utilities: Methods for validation, conversion, and display

Installation

npm install @andrewdonelson/byte-enum
# or
yarn add @andrewdonelson/byte-enum

Pros & Cons

Pros

  • Memory Optimized: Uses 1-4 bytes per value versus 8+ bytes for string enums
  • Runtime Utilities: Provides methods not available with native TypeScript enums
  • Database Friendly: Smaller storage requirements for enum columns
  • Network Efficient: Reduces payload size when transmitting data
  • Fast Lookups: Optimized for performance with Map-based implementation
  • User Friendly: Easily convert between code values and human-readable formats
  • Type Safe: Full TypeScript integration

Cons

  • Not Standard: Requires using a custom implementation instead of native TypeScript enums
  • Setup Overhead: Slightly more code to define compared to native enums
  • Value Limits: Limited to 256 values per enum (though this is rarely an issue)
  • Tooling: IDEs may not provide the same level of auto-completion as with native enums

Usage

Creating Enums

import { createByteEnum, createCharEnum, createAlphaNumEnum, TinyEnumValue } from '@andrewdonelson/byte-enum';

// Using byte values (0, 1, 2, etc.)
export const BodyType = createByteEnum([
  'PETITE',
  'ATHLETIC',
  'AVERAGE', 
  'MUSCULAR',
  'PLUS_SIZED',
  'CURVY'
] as const);

// Get the type for TypeScript
export type BodyTypeValue = TinyEnumValue<typeof BodyType>;

// Using character values (single characters)
export const Status = createCharEnum([
  'ACTIVE',
  'INACTIVE',
  'PENDING',
  'SUSPENDED'
] as const);

// Using alphanumeric characters (0-9, a-z, A-Z only)
export const Priority = createAlphaNumEnum([
  'LOW',
  'MEDIUM',
  'HIGH',
  'CRITICAL'
] as const);

Accessing Enum Values

// Get the enum value (just like with native enums)
const bodyType = BodyType.ATHLETIC; // Returns 1

// Use in interfaces/types
interface Profile {
  name: string;
  bodyType: BodyTypeValue; // Type is number (byte)
}

// Creating an object with the enum
const profile: Profile = {
  name: "John",
  bodyType: BodyType.ATHLETIC // Storing the value 1
};

Utility Methods

// Get all possible values
const allBodyTypes = BodyType.values(); // [0, 1, 2, 3, 4, 5]

// Get all keys
const allBodyTypeKeys = BodyType.keys(); // ["PETITE", "ATHLETIC", ...]

// Check if a value is valid
if (BodyType.isValue(userInput)) {
  // userInput is a valid body type
}

// Convert from value to key
const keyName = BodyType.getKeyByValue(profile.bodyType); // "ATHLETIC"

// Get display name (formatted)
const displayName = BodyType.getDisplayName(profile.bodyType); // "Athletic"

// Custom formatting
const customName = BodyType.getFormattedName(
  profile.bodyType, 
  (key) => `Body Type: ${key.toLowerCase()}`
); // "Body Type: athletic"

Use Cases

Database Storage

// Database schema (using TypeScript to represent)
interface UserRecord {
  id: string;
  name: string;  
  body_type: number; // Stores a single byte (0-255)
  status: string;   // Stores a single character
}

// Converting to database format
function saveUser(profile: Profile): UserRecord {
  return {
    id: profile.id,
    name: profile.name,
    body_type: profile.bodyType, // Single byte value
    status: profile.status       // Single character
  };
}

API Communication

// Compact API payload
const apiPayload = {
  users: [
    { id: "user1", name: "John", bt: BodyType.ATHLETIC, st: Status.ACTIVE },
    { id: "user2", name: "Lisa", bt: BodyType.PETITE, st: Status.PENDING }
  ]
};

// Converting from API response
function parseApiResponse(data: any[]): Profile[] {
  return data.map(item => ({
    id: item.id,
    name: item.name,
    bodyType: BodyType.isValue(item.bt) ? item.bt : BodyType.AVERAGE,
    status: Status.isValue(item.st) ? item.st : Status.PENDING
  }));
}

Large Collections

// Memory-efficient storage for millions of records
const userBodyTypes: BodyTypeValue[] = new Array(1000000);

// Fill with random values
for (let i = 0; i < userBodyTypes.length; i++) {
  userBodyTypes[i] = Math.floor(Math.random() * 6); // Random body type
}

// Memory usage: ~8MB vs ~50-100MB with string values

Advanced Usage

Custom Character Sets

// Using specific meaningful characters
export const OrderStatus = createCharEnum([
  'PENDING',
  'PROCESSING',
  'SHIPPED',
  'DELIVERED',
  'CANCELLED'
], 'PPSDC'); // Maps to these specific characters

Custom Formatters

// Define a custom formatter
function genderFormatter(key: string): string {
  switch(key) {
    case 'MALE': return 'Man';
    case 'FEMALE': return 'Woman';
    case 'NON_BINARY': return 'Non-binary person';
    default: return key;
  }
}

// Use the custom formatter
const displayGender = Gender.getFormattedName(profile.gender, genderFormatter);

TypeScript Integration

// Exhaustive switch statements
function getBodyTypeDescription(bodyType: BodyTypeValue): string {
  switch (bodyType) {
    case BodyType.PETITE:
      return "Small frame";
    case BodyType.ATHLETIC:
      return "Toned muscles";
    case BodyType.AVERAGE:
      return "Balanced proportions";
    case BodyType.MUSCULAR:
      return "Strong build";
    case BodyType.PLUS_SIZED:
      return "Fuller figure";
    case BodyType.CURVY:
      return "Defined curves";
    default:
      // TypeScript ensures we've covered all cases
      const exhaustiveCheck: never = bodyType;
      return exhaustiveCheck;
  }
}

Performance Considerations

ByteEnum is optimized for both memory usage and lookup performance. Here's how it compares to native TypeScript enums:

Memory Usage (for 1 million values):

  • Native String Enum: ~15-30MB
  • Native Numeric Enum: ~8MB
  • ByteEnum Byte Enum: ~1-4MB
  • ByteEnum Char Enum: ~1-4MB

Lookup Performance:

  • Native Enum Direct Access: Fastest
  • ByteEnum Direct Access: Nearly identical
  • Native Enum Reverse Lookup: O(1) for numeric enums only
  • ByteEnum Reverse Lookup: O(1) using Map-based implementation

Examples

User Profile System

import { createByteEnum, createCharEnum, TinyEnumValue } from '@andrewdonelson/byte-enum';

// Define gender enum
const Gender = createByteEnum([
  'MALE',
  'FEMALE',
  'NON_BINARY',
  'OTHER',
  'PREFER_NOT_TO_SAY'
] as const);
type GenderValue = TinyEnumValue<typeof Gender>;

// Define account status enum
const AccountStatus = createCharEnum([
  'ACTIVE',
  'SUSPENDED',
  'PENDING_VERIFICATION',
  'CLOSED'
] as const);
type AccountStatusValue = TinyEnumValue<typeof AccountStatus>;

// Define user interface
interface User {
  id: string;
  name: string;
  gender: GenderValue;
  status: AccountStatusValue;
}

// Create a user
const user: User = {
  id: 'user123',
  name: 'Alex Smith',
  gender: Gender.NON_BINARY,      // Stores 2 (byte)
  status: AccountStatus.ACTIVE    // Stores a single character
};

// Display user info
function renderUserProfile(user: User): string {
  return `
    Name: ${user.name}
    Gender: ${Gender.getDisplayName(user.gender)}
    Status: ${AccountStatus.getDisplayName(user.status)}
  `;
}

// Convert for database storage
function toDbRecord(user: User) {
  return {
    id: user.id,
    name: user.name,
    gender_code: user.gender,            // Store as byte
    account_status_code: user.status     // Store as character
  };
}

// UI dropdown options
function getGenderOptions() {
  return Gender.values().map(value => ({
    value,
    label: Gender.getDisplayName(value)
  }));
}

Order Processing System

import { createCharEnum, TinyEnumValue } from '@andrewdonelson/byte-enum';

// Define order status with custom characters
const OrderStatus = createCharEnum([
  'PENDING',
  'PROCESSING',
  'SHIPPED',
  'DELIVERED',
  'CANCELLED',
  'RETURNED',
  'REFUNDED'
], 'PSDCXRF'); // Meaningful character codes
type OrderStatusValue = TinyEnumValue<typeof OrderStatus>;

// Define permitted status transitions
const allowedTransitions: Record<OrderStatusValue, OrderStatusValue[]> = {
  [OrderStatus.PENDING]: [OrderStatus.PROCESSING, OrderStatus.CANCELLED],
  [OrderStatus.PROCESSING]: [OrderStatus.SHIPPED, OrderStatus.CANCELLED],
  [OrderStatus.SHIPPED]: [OrderStatus.DELIVERED, OrderStatus.RETURNED],
  [OrderStatus.DELIVERED]: [OrderStatus.RETURNED],
  [OrderStatus.CANCELLED]: [OrderStatus.REFUNDED],
  [OrderStatus.RETURNED]: [OrderStatus.REFUNDED],
  [OrderStatus.REFUNDED]: []
};

// Use in business logic
function canTransitionStatus(current: OrderStatusValue, target: OrderStatusValue): boolean {
  return allowedTransitions[current]?.includes(target) || false;
}

// Usage
const order = {
  id: 'ORD-123',
  status: OrderStatus.PROCESSING // 'S' character
};

// Check if transition is valid
if (canTransitionStatus(order.status, OrderStatus.SHIPPED)) {
  order.status = OrderStatus.SHIPPED;
}

// Display order status
console.log(`Order status: ${OrderStatus.getDisplayName(order.status)}`);

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Package Sidebar

Install

npm i @andrewdonelson/byte-enum

Weekly Downloads

0

Version

1.0.0

License

MIT

Unpacked Size

24.4 kB

Total Files

7

Last publish

Collaborators

  • nlaakald