Skip to content

Latest commit

Β 

History

History
236 lines (175 loc) Β· 7.14 KB

File metadata and controls

236 lines (175 loc) Β· 7.14 KB

enums

  • πŸ“¦ Below 335 Bytes minified + compressed (brotli)
  • βœ… Zero dependencies

JS-only alternative with minimal runtime footprint for TypeScript's enum when you want to use erasableSyntaxOnly (read here why). Mimics the behavior of string-based enum.

Basic usage

import { enums, type Enums } from "@peerigon/typescript-toolkit/enums";

const Direction = enums.define({
  /** Add JSDoc comments here to explain each option */
  North: true, // true means that the key name is used as the value
  South: true,
  East: true,
  West: true,
});
// Derive the union type of all enum values
type Direction = Enums<typeof Direction>;

// Hovering over Direction.South shows the JSDoc
console.log(Direction.South); // "South"

// Combine with match() for pattern matching and exhaustiveness checks
const oppositeDirection = match(direction).case([
  // Shows a type error here because not all cases have been implemented
  [Direction.North, Direction.South],
  [Direction.South, Direction.North],
  [Direction.East, Direction.West],
]);

Custom enum values

// Mix different value types
const Status = enums.define({
  Active: "Active",
  Inactive: 0,
  Unknown: Symbol("unknown"),
});
type Status = Enums<typeof Status>;

console.log(Status.Active); // "Active"
console.log(Status.Inactive); // 0
console.log(Status.Unknown); // Symbol(unknown)

Type safety

enums.define creates "branded" types for each option. This means that you must reference the enum property and can't assign the primitive value directly:

const Color = enums.define({
  Red: true,
  Green: true,
});
type Color = Enums<typeof Color>;

let color: Color;

color = Color.Red; // βœ… Valid
color = "Red"; // ❌ TypeScript error, because Color.Red and Color.Green are branded

By default, all enums are branded with the same symbol. This means that another enum with the same primitive value can be assigned:

const Status = enums.define({
  Red: true, // Same value as Color.Red
  Green: true, // Same value as Color.Green
});
type Status = Enums<typeof Status>;

// πŸ₯΄ Will work because Status.Red and Color.Red use "Red"
// as primitive value and the same default symbol as brand... :/
color = Status.Red;

Although not ideal, this shouldn't be a problem in practice because you typically don't mix enums like this. For maximum type safety, you can use branded enums (see below).

Branded Enums

Use enums.define.branded() to create enums that cannot be mixed even if they have identical values:

// Two enums with same values but different brands
const ColorBrand = Symbol("Color");
const Color = enums.define.branded(ColorBrand, {
  Red: true,
  Green: true,
});
type Color = Enums<typeof Color>;

const StatusBrand = Symbol("Status");
const Status = enums.define.branded(StatusBrand, {
  Red: true, // Same value as Color.Red
  Green: true, // Same value as Color.Green
});
type Status = Enums<typeof Status>;

let color: Color;
let status: Status;

color = Color.Red; // βœ… Valid
color = Status.Red; // ❌ TypeScript error - cannot mix branded enums

status = Status.Red; // βœ… Valid
status = Color.Red; // ❌ TypeScript error - cannot mix branded enums

Parsing and Validation

Use enums.parse() to validate that a value belongs to a specific enum:

const Direction = enums.define({
  North: true,
  South: true,
  East: true,
  West: true,
});
type Direction = Enums<typeof Direction>;

// Parse and validate enum values
function processDirection(input: unknown) {
  try {
    const direction = enums.parse(Direction, input);
    // direction is now typed as Direction
    console.log(`Valid direction: ${direction}`);
    return direction;
  } catch (error) {
    // error.cause contains { enum: Direction, value: input }
    console.error(`Invalid direction: ${input}`);
    throw error;
  }
}

processDirection("North"); // βœ… Returns Direction.North
processDirection("Northeast"); // ❌ Throws TypeError
processDirection(42); // ❌ Throws TypeError

Iteration and Introspection

Use enums.entries() to get key-value pairs for iteration:

const Status = enums.define({
  Active: "active",
  Inactive: 0,
  Pending: true,
});
type Status = Enums<typeof Status>;

// Get all [key, value] tuples
const entries = enums.entries(Status);
// [["Active", "active"], ["Inactive", 0], ["Pending", "Pending"]]

API Reference

enums.define(definition)

Creates a type-safe enum from an object definition.

enums.define<Definition>(definition: Definition): EnumDefinition
Parameter Type Description
definition Record<string, unknown> Keys become enum names; values become enum values. Use true to use the key as the value

Returns: Frozen enum object with type-safe values

enums.define.branded(symbol, definition)

Creates a branded enum that cannot be mixed with other enums.

enums.define.branded<Brand, Definition>(symbol: Brand, definition: Definition): EnumDefinition<Brand, Definition>
Parameter Type Description
symbol symbol Unique symbol branding this enum type
definition Record<string, unknown> Keys and values defining the enum (same rules as enums.define)

Returns: Frozen branded enum object

enums.parse(definition, value)

Validates that value is a valid enum value and returns it with the correct type.

enums.parse<Definition>(definition: Definition, value: unknown): Enums<Definition>
Parameter Type Description
definition Definition Enum object created with enums.define
value unknown Value to validate

Returns: Enums<Definition>

Throws: TypeError if value is invalid; cause is { enum, value }

enums.entries(definition)

Returns [key, value] tuples from an enum definition.

enums.entries<Definition>(definition: Definition): Array<[keyof Definition, Enums<Definition>]>
Parameter Type Description
definition Definition Enum object created with enums.define

Returns: Array of [key, value] tuples (property names and enum values)

Enums<Definition>

Type utility for the union of all enum values.

type Enums<Definition> = Definition[keyof Definition];
Type parameter Description
Definition Enum definition object type