New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

type-safe-enum

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

type-safe-enum

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

latest
Source
npmnpm
Version
1.2.4
Version published
Maintainers
1
Created
Source

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

FeatureNative EnumString UnionsConst Objectstype-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:

  • Enum Object: The container that holds all enum values and utility methods (e.g., Status, UserRole)
  • 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-indexed: 0
  MEDIUM: { value: 'medium', index: 10 },  
  HIGH: { value: 'high' }                 // auto-indexed: 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!';
}

function greetFromApi(role: unknown): string {
  if (!UserRole.isEnumValue(role)) {
    return 'Welcome!';
  }
  return greet(role);
}

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

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

// Iterate over entries
for (const [key, role] of UserRole.getEntries()) {
  console.log(`${key}: ${role.value} (${role.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

MethodDescriptionExample
fromValue(value: string): SafeEnum<TypeName>Returns the enum value matching the given value, or undefined if not foundGet enum value by string value
fromKey(key: string): SafeEnum<TypeName>Returns the enum value matching the given key, or undefined if not foundGet enum value by key
fromIndex(index: number): SafeEnum<TypeName>Returns the enum value matching the given index, or undefined if not foundGet enum value by index
hasValue(value: string): booleanReturns true if the enum has a value matching the given value, false otherwiseCheck if value exists
hasKey(key: string): booleanReturns true if the enum has a key matching the given key, false otherwiseCheck if key exists
hasIndex(index: number): booleanReturns true if the enum has an index matching the given index, false otherwiseCheck if index exists
isEnumValue(value: unknown): value is SafeEnum<TypeName>Returns true if the value is from this enum, false otherwiseType guard: Check if value is from this enum
isEqual(other: SafeEnum<TypeName> | SafeEnum<TypeName>[]): booleanSingle value: true if it matches a member of the enum. Array: true if all items are equal to the first.if (UserRole.isEqual([role1, role2])) { ... }
toString(): stringGet string representation of the enum objectUserRole.toString()
toJSON(): { typeName: string, values: Array<{ key: string, value: string, index: number }> }Get JSON-serializable representation of the enumUserRole.toJSON()
getValues(): string[]Get all enum values as stringsUserRole.getValues()
getIndexes(): number[]Get all enum indices as numbersUserRole.getIndexes()
getEntries(): [string, SafeEnum<TypeName>][]Get all [key, value] pairsUserRole.getEntries()
getKeys(): string[]Get all enum keys as stringsUserRole.getKeys()
getKey(): stringGet the first enum member's keyUserRole.getKey()
getValue(): stringGet the first enum member's valueUserRole.getValue()
getIndex(): numberGet the first enum member's indexUserRole.getIndex()
[Symbol.iterator](): IterableIterator<SafeEnum<TypeName>>Iterate enum values (supports for...of / Array.from)Array.from(UserRole)

Instance Methods

MethodDescriptionExample
hasValue(value: string): booleanCheck if this enum value has the given valueUserRole.ADMIN.hasValue('admin')
hasKey(key: string): booleanCheck if this enum value has the given keyUserRole.ADMIN.hasKey('ADMIN')
hasIndex(index: number): booleanCheck if this enum value has the given indexUserRole.ADMIN.hasIndex(10)
isEnumValue(value: unknown): value is SafeEnum<TypeName>Type guard: Check if value is from the same enumUserRole.ADMIN.isEnumValue(role)
isEqual(other: SafeEnum<TypeName> | SafeEnum<TypeName>[]): booleanCompare with another enum value or array of valuesUserRole.ADMIN.isEqual(otherRole)
toString(): stringGet string representation in format "KEY: value, index: N"UserRole.ADMIN.toString()
toJSON(): { key: string, value: string, index: number }Get JSON-serializable objectUserRole.ADMIN.toJSON()
getKeyOrThrow(): stringGet the key of the enum value (throws if missing)UserRole.ADMIN.getKeyOrThrow()
getValueOrThrow(): stringGet the value of the enum value (throws if missing)UserRole.ADMIN.getValueOrThrow()
getIndexOrThrow(): numberGet the index of the enum value (throws if missing)UserRole.ADMIN.getIndexOrThrow()

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."

Keywords

typescript

FAQs

Package last updated on 05 Jan 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts