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

type-safe-enum

v1.2.3

Published

A type-safe, flexible enum factory for TypeScript with runtime validation and type inference

Readme

Type-safe-enum

npm version License: MIT GitHub Workflow Status codecov Bundle Size TypeScript

A type-safe, flexible enum factory for TypeScript with runtime validation and type inference. Create robust enums with minimal boilerplate while maintaining full type safety. This package provides a more type-safe alternative to TypeScript's native enums with additional features like runtime validation, bi-directional mapping, and better type inference.

🚀 Performance & Benefits

Performance Optimizations

  • Blazing Fast Lookups - O(1) constant time for all lookup operations:

    • fromValue(), fromKey(), fromIndex()
    • hasValue(), hasKey(), hasIndex()
    • getKey(), getValue(), getIndex()
  • Efficient Iteration - O(n) linear time for collection operations:

    • getEntries(), getKeys(), getValues(), getIndexes()
  • Minimal Memory Footprint

    • Uses Map for O(1) lookups by value, key, and index
    • No prototype pollution - clean object structure
    • Values are cached and reused

✨ Key Benefits

  • Type Safety First - Full TypeScript support with strict type checking
  • Runtime Validation - Verify enum values at runtime
  • Zero Dependencies - No external packages required
  • Tiny Bundle - ~2KB minified + gzipped
  • Tree-shakeable - Only includes what you use
  • Immutable by Design - Uses Object.freeze() to prevent modifications
  • Auto-completion - Full IntelliSense support in modern IDEs
  • Serialization Ready - Built-in toJSON() and toString() methods
  • Comprehensive API - All the utility methods you need
  • Battle-tested - Tested with Vitest to ensure 100% test coverage

Comparison with Other Approaches

| Feature | Native Enum | String Unions | Const Objects | type-safe-enum | | ------------------ | -------------------------------------------------- | ------------------------------------ | ---------------------------------------------- | ------------------------------------- | | Type Safety | | | ⚠️(requires care) | (nominal typing) | | Runtime Safety | | | | | | IntelliSense | | | | | | Reverse Lookup | (but unsafe) | | | | | JSON Serialization | (numeric issues) | | | | | Maintenance | (verbose) | | | | | String Comparison | (can be confusing) | | | | | Iteration | | | | | | Bundle Size | (0kB) | (0kB) | (0kB) | (~2kB) | | Tree Shaking | | | | |

Native Enum

enum Role {
  ADMIN = 'admin',
  EDITOR = 'editor',
  VIEWER = 'viewer'
}
/* 
* Can't easily validate a string
* Enums are compiled weirdly (numeric fallbacks, bi-directional maps)
* Serialization/deserialization is clumsy 
*/
function isValidRole(role: string): boolean {
  return Object.values(Role).includes(role as Role); // not type-safe
}

String Unions

// String Unions: No runtime validation or utilities
type Role = 'admin' | 'editor' | 'viewer';
/*
* Repeating the list of values manually everywhere
* Can't iterate roles cleanly
* No structure beyond flat string
*/
function isValidRole(role: string): role is Role {
  return ['admin', 'editor', 'viewer'].includes(role); // hardcoded list
}

Const Objects

// Const Objects: No runtime validation or utilities
const Role = {
  ADMIN: 'admin',
  EDITOR: 'editor',
  VIEWER: 'viewer'
} as const;

type Role = typeof Role[keyof typeof Role];

/*
* Still no .fromValue() or .fromKey()
* No index, no rich object model
* No safe .map()/.entries(), just raw Object.entries()
* No default constObject.stringify() support
* Some IntelliSense support
* No reverse lookups
* No automatic indexing
*/
function isValidRole(role: string): role is Role {
  return Object.values(Role).includes(role as Role); // still loose
}

type-safe-enum

import { CreateSafeEnumFromArray, type SafeEnum } from 'type-safe-enum';

/*
* Full runtime validation
* Safe .fromValue() / .fromKey()
* Safe .map() / .entries()
* safeEnum.toJSON() support
* Tree-shaking
* Full IntelliSense support
* Reverse lookups
* Automatic indexing
*/
const Status = 
  CreateSafeEnumFromArray(["Pending", "Approved", "Rejected"] as const, "Status");
// Type for enum values with nominal typing
type StatusType = SafeEnum<"Status">;

// Usage
const currentStatus: StatusType = Status.PENDING;
console.log(currentStatus.value);  // 'Pending'
console.log(currentStatus.index);  // 0 (auto-assigned)

// Type-safe lookups
const approved = Status.fromValue("Approved");  // Status.APPROVED | undefined
const pending = Status.fromKey("PENDING");      // Status.PENDING | undefined

// Type guards
if (Status.hasValue("Pending")) {
  // TypeScript knows this is valid
  const status: Status = Status.fromValue("Pending")!;
}

// Type-safe usage in functions
function processStatus(status: StatusType): string {
  if (status.isEqual(Status.APPROVED)) {
    return 'Approved!';
  }
  return 'Not approved!';
}

// Type usage in your code
function checkAccess(role: StatusType): boolean {
  return role.isEqual(Status.APPROVED);
}

Requirements

  • Node.js >= 16.0.0
  • TypeScript >= 4.9.0
  • npm >= 7.0.0 or yarn >= 1.22.0 or pnpm >= 6.0.0

Installation

npm install type-safe-enum
# or
yarn add type-safe-enum
# or
pnpm add type-safe-enum

Type System Overview

This library provides two main concepts:

  1. Enum Object: The container that holds all enum values and utility methods (e.g., Status, UserRole)
  2. Enum Value: A single value from the enum (e.g., Status.PENDING, UserRole.ADMIN)

Key Types

  • SafeEnum<TypeName>: Interface for a single enum value with nominal typing (contains key, value, index, and __typeName)
  • SafeEnumObject<TypeName>: Interface for the enum object (contains all values and utility methods)

Cross-Module Compatibility

The library is designed to work seamlessly across module boundaries in monorepos and complex TypeScript projects. Both CreateSafeEnum and CreateSafeEnumFromArray return SafeEnumObject<TypeName>, ensuring portable type definitions that avoid TS2742 errors when exporting enums between packages.

// ✅ Works in cross-module scenarios
import { CreateSafeEnumFromArray, type SafeEnum } from "type-safe-enum"

export const envList = CreateSafeEnumFromArray(
  ["development", "test", "testing", "production"],
  "EnvType"
)
export type EnvType = SafeEnum<"EnvType">

Quick Start Guide

1. Enum with Custom Values and Indices

import { CreateSafeEnum, type SafeEnumObject } from 'type-safe-enum';
import type { SafeEnum } from 'type-safe-enum';
// Create an enum with custom values and indices
const UserRole: SafeEnumObject<"UserRole"> = CreateSafeEnum({
  ADMIN: { value: 'admin', index: 10 },    // Explicit index
  EDITOR: { value: 'editor', index: 13 },  // Explicit index
  VIEWER: { value: 'viewer' },             // Auto-assigns next index (14)
} as const, "UserRole");

type UserRoleType = SafeEnum<"UserRole">;

// Mixed explicit and auto indexes
const Priority = CreateSafeEnum({
  LOW: { value: 'low'},                   // auto: 0
  MEDIUM: { value: 'medium', index: 10 },  
  HIGH: { value: 'high' }                 // auto: 11
}, "Priority");
type PriorityType = SafeEnum<"Priority">;

// Usage examples
const admin: UserRoleType = UserRole.ADMIN;
console.log(admin.key);     // 'ADMIN'
console.log(admin.value);   // 'admin'
console.log(admin.index);   // 10

// Type-safe usage in functions
function greet(userRole: UserRoleType): string {
  if (userRole.isEqual(UserRole.ADMIN)) {
    return 'Hello Admin!';
  }
  return 'Welcome!';
}

// Type-safe lookups
const isValid = UserRole.hasValue('admin');  // true

// Get all values, keys, and indexes
const allValues = UserRole.values();    // ['admin', 'editor', 'viewer']
const allKeys = UserRole.keys();        // ['ADMIN', 'EDITOR', 'VIEWER']
const allIndexes = UserRole.indexes();  // [10, 13, 14]

// Iterate over entries
for (const [key, value] of UserRole.entries()) {
  console.log(`${key}: ${value.value} (${value.index})`);
}

Real-World Examples

API Response Validation

import { CreateSafeEnum, type SafeEnum } from 'type-safe-enum';

const StatusCode = CreateSafeEnum({
  OK: { value: 'ok', index: 200 },
  CREATED: { value: 'created', index: 201 },
  BAD_REQUEST: { value: 'bad_request', index: 400 },
  UNAUTHORIZED: { value: 'unauthorized', index: 401 },
  NOT_FOUND: { value: 'not_found', index: 404 },
  SERVER_ERROR: { value: 'server_error', index: 500 },
} as const, "StatusCode");

type StatusCodeType = SafeEnum<"StatusCode">;

// Type-safe status code handling
function handleResponse(statusCode: number): string {
  const status = StatusCode.fromIndex(statusCode);
  if (!status) return 'Unknown status';
  
  if (status.index === StatusCode.OK.index) {
    return 'Success!';
  } else if (status.isEqual(StatusCode.UNAUTHORIZED)) {
    return 'Please log in';
  }
  return `Error: ${status.value}`;
}

// Example usage
const responseCode: StatusCodeType = StatusCode.OK;
console.log(handleResponse(200)); // 'Success!'
console.log(handleResponse(401)); // 'Please log in'

Form State Management

import { CreateSafeEnum, type SafeEnum } from 'type-safe-enum';
import { useState } from 'react';

const FormState = CreateSafeEnum({
  IDLE: { value: 'idle', index: 10 },
  SUBMITTING: { value: 'submitting' },  // auto-indexed 11
  SUCCESS: { value: 'success', index: 20 },
  ERROR: { value: 'error' },            // auto-indexed 21
} as const, "FormState");

type FormStateType = SafeEnum<"FormState">;

function FormComponent() {
  const [state, setState] = useState<FormStateType>(FormState.IDLE);
  
  // Example usage in event handlers
  const handleSubmit = async () => {
    try {
      setState(FormState.SUBMITTING);
      await submitForm();
      setState(FormState.SUCCESS);
    } catch (error) {
      setState(FormState.ERROR);
    }
  };
  
  return (
    <div>
      {state.isEqual(FormState.SUBMITTING) && <Spinner />}
      {state.isEqual(FormState.ERROR) && <ErrorBanner />}
      {state.isEqual(FormState.SUCCESS) && <SuccessMessage />}
      <button 
        onClick={handleSubmit}
        disabled={state.isEqual(FormState.SUBMITTING)}
      >
        Submit
      </button>
    </div>
  );
}

API Reference

CreateSafeEnum(enumMap): SafeEnumObject<TypeName>

Creates a type-safe enum from an object mapping.

CreateSafeEnumFromArray(values): SafeEnumObject<TypeName>

Creates a type-safe enum from an array of string literals.

Static Methods

| Method | Description | Example | |------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|--------------------------------------------------------------| | fromValue(value: string): SafeEnum<TypeName> | undefined | Get enum value by string value | UserRole.fromValue('admin') | | fromKey(key: string): SafeEnum<TypeName> | undefined | Get enum value by key | UserRole.fromKey('ADMIN') | | fromIndex(index: number): SafeEnum<TypeName> | undefined | Get enum value by index | UserRole.fromIndex(0) | | hasValue(value: string): boolean | Check if value exists | UserRole.hasValue('admin') | | hasKey(key: string): boolean | Check if key exists | UserRole.hasKey('ADMIN') | | hasIndex(index: number): boolean | Check if index exists | UserRole.hasIndex(0) | | isEqual(values: [SafeEnum<TypeName>, ...SafeEnum<TypeName>[]]): boolean | Static method: Check if ALL enum values are equal | if (UserRole.isEqual([role1, role2])) { ... } | | isEqual(    value: SafeEnum<TypeName> |     SafeEnum<TypeName>[]): boolean</div> | Instance method: Check if enum value equals ANY in array | role.isEqual([UserRole.ADMIN, UserRole.EDITOR]): boolean| |getValues(): string[] | Get all enum values as strings |UserRole.getValues() | |getIndexes(): number[] | Get all enum indices as numbers |UserRole.getIndexes() | |getEntries(): [string, SafeEnumValue][] | Get all [key, value] pairs |UserRole.getEntries() | |getKeys(): string[] | Get all enum keys as strings |UserRole.getKeys()` |

Instance Methods

| Method | Description | Example | | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------- | | isEqual(    other: SafeEnumValue<T> \|     SafeEnumValue<T>[]): boolean</div> | Compare with another enum value or array of values | UserRole.ADMIN.isEqual(otherRole)| |toString(): string | Get string representation in format"KEY: (value), index: N"|UserRole.ADMIN.toString() | |toJSON(): { key: string, value: string, index: number } | Get JSON-serializable object |UserRole.ADMIN.toJSON() | |getIndex(): number | Get the index of the enum value or Throws if undefined |UserRole.ADMIN.getIndex() | |getKey(): string | Get the key of the enum value or Throws if undefined |UserRole.ADMIN.getKey() | |getValue(): string | Get the value of the enum value or Throws if undefined |UserRole.ADMIN.getValue()` |

License

MIT © Elfre Valdes

Acknowledgments

  • Jarom Loveridge (Helped improve type definitions)
  • David Jones
  • Scott Thorsen
  • Charles Hugo

ChatGPT's Opinion on type-safe-enum

"Unlike traditional TypeScript enums, which can be opaque and error-prone (especially with numeric values and reverse mappings), type-safe-enum uses object literals or classes to infer literal union types that are transparent, predictable, and safe. It embraces the full power of TypeScript's type system to ensure better IntelliSense, stricter compile-time checks, and improved maintainability — particularly in large codebases and shared libraries.

I highly recommend type-safe-enum over native enums for most modern TypeScript projects. It's a cleaner, more reliable way to define constants and enum-like structures, without the pitfalls of traditional enums."