Skip to main content
The enum_() decoder validates TypeScript enum values, handling both string and numeric enums correctly.

Signature

function enum_<TEnum extends Record<string, string | number>>(
  enumObj: TEnum
): Decoder<TEnum[keyof TEnum]>
The function is named enum_ (with underscore) because enum is a reserved keyword in JavaScript.

Basic Usage

String Enums

import { enum_ } from 'decoders';

enum Color {
  Red = 'red',
  Green = 'green',
  Blue = 'blue',
}

const colorDecoder = enum_(Color);

colorDecoder.verify('red');    // ✓ Color.Red
colorDecoder.verify('green');  // ✓ Color.Green
colorDecoder.verify('yellow'); // ✗ Error: Must be one of "red", "green", "blue"

Numeric Enums

import { enum_ } from 'decoders';

enum Status {
  Pending = 0,
  Active = 1,
  Complete = 2,
}

const statusDecoder = enum_(Status);

statusDecoder.verify(0);  // ✓ Status.Pending
statusDecoder.verify(1);  // ✓ Status.Active
statusDecoder.verify(3);  // ✗ Error: Must be one of 0, 1, 2

Const Enums

import { enum_ } from 'decoders';

const enum Direction {
  North = 'N',
  South = 'S',
  East = 'E',
  West = 'W',
}

const directionDecoder = enum_(Direction);

directionDecoder.verify('N');  // ✓ Direction.North
directionDecoder.verify('X');  // ✗ Error

Type Inference

The decoder correctly infers the enum type:
import { enum_, type DecoderType } from 'decoders';

enum Priority {
  Low = 'low',
  Medium = 'medium',
  High = 'high',
}

const priorityDecoder = enum_(Priority);

type PriorityType = DecoderType<typeof priorityDecoder>;
// type PriorityType = Priority.Low | Priority.Medium | Priority.High
// which is equivalent to: 'low' | 'medium' | 'high'

const priority = priorityDecoder.verify('high');
// priority: Priority

Mixed Enums

For enums with both string and numeric values:
import { enum_ } from 'decoders';

enum Mixed {
  Zero = 0,
  One = 1,
  Default = 'default',
  Auto = 'auto',
}

const mixedDecoder = enum_(Mixed);

mixedDecoder.verify(0);         // ✓ Mixed.Zero
mixedDecoder.verify('default'); // ✓ Mixed.Default
mixedDecoder.verify(2);         // ✗ Error
mixedDecoder.verify('other');   // ✗ Error

With Objects

Enums work well in object decoders:
import { enum_, object, string, number } from 'decoders';

enum Role {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
}

enum Plan {
  Free = 0,
  Pro = 1,
  Enterprise = 2,
}

const userDecoder = object({
  name: string,
  role: enum_(Role),
  plan: enum_(Plan),
});

const user = userDecoder.verify({
  name: 'Alice',
  role: 'admin',
  plan: 1,
});
// user.role is Role
// user.plan is Plan

Error Messages

The decoder provides clear error messages:
import { enum_ } from 'decoders';

enum Status {
  Draft = 'draft',
  Published = 'published',
}

const decoder = enum_(Status);

decoder.verify('archived');
// Error: Must be one of "draft", "published"

vs oneOf()

For enum values, prefer enum_() over oneOf():
import { enum_, oneOf } from 'decoders';

enum Color { Red = 'red', Green = 'green', Blue = 'blue' }

// ✓ Preferred: enum_() handles enums correctly
const decoder1 = enum_(Color);

// ✗ Works but less clear:
const decoder2 = oneOf(['red', 'green', 'blue'] as const);

// enum_() correctly handles:
// - Numeric enums
// - Reverse mapping in numeric enums
// - Mixed enums

Implementation Details

The enum_() function handles TypeScript’s enum quirks:
  • String enums: Direct value matching
  • Numeric enums: Filters out reverse mappings (0 → 'Zero' entries)
  • Mixed enums: Combines both strategies
enum Example {
  Zero = 0,
  One = 1,
}
// Compiles to: { Zero: 0, One: 1, 0: 'Zero', 1: 'One' }
// enum_() correctly extracts: [0, 1]

Real-World Example

import { enum_, object, string, number, optional } from 'decoders';

enum OrderStatus {
  Pending = 'pending',
  Processing = 'processing',
  Shipped = 'shipped',
  Delivered = 'delivered',
  Cancelled = 'cancelled',
}

enum PaymentMethod {
  Card = 0,
  PayPal = 1,
  BankTransfer = 2,
}

const orderDecoder = object({
  id: string,
  status: enum_(OrderStatus),
  payment: enum_(PaymentMethod),
  total: number,
  notes: optional(string),
});

// Validate API response
const order = orderDecoder.verify({
  id: 'ORD-123',
  status: 'processing',
  payment: 0,
  total: 99.99,
});

// order.status is OrderStatus.Processing
// order.payment is PaymentMethod.Card
  • oneOf() - For literal value unions without enums
  • constant() - For single constant values
  • either() - For combining different decoder types

Build docs developers (and LLMs) love