enumwaii

Member surfaces

Choose between .enum, .values, .rawEnum, .rawValues, and .cases.

An enumwaii declaration exposes several typed views of the same closed set. Each view serves a specific application or integration role.

const  = (["ADMIN", "USER"]);
const  = .;
const  = .;
const  = .;

.; // branded application value
.; // branded member tuple
.; // raw literal "ADMIN"
.; // raw literal tuple
.; // raw discriminant "ADMIN"

.enum: the default

Use .enum for application values, comparisons, defaults, fixtures, function arguments, and derivation.

const  = .;

function (: (typeof )["~type"]) {}

(.);
("ADMIN"); // TypeScript error
Argument of type '"ADMIN"' is not assignable to parameter of type '("ADMIN" & EnumwaiiBrand<"enumwaii:ADMIN" | "enumwaii:USER", "ADMIN">) | ("USER" & EnumwaiiBrand<"enumwaii:ADMIN" | "enumwaii:USER", "USER">)'.

.enum is the standard surface for trusted application code.

For the object overload, property names and canonical values may differ:

import {  } from "enumwaii";

const  = ({
  : "order-paid",
  : "order-pending",
});
const  = .;

.; // branded "order-paid"
.("order-paid"); // valid
.("ORDER_PAID"); // throws

The keys are only the developer-facing object surface. .values, .rawValues, parsing, schemas, adapters, and derivation all use the mapped values.

Member-level JSDoc is preserved from object properties to hover information on .enum members. Use a fully documented identity map such as em({ /** ... */ READY: "READY" }) when every member needs documentation. TypeScript does not expose tuple-element comments through mapped property symbols, so comments inside em([/* ... */ "READY"]) cannot appear on enum.READY. The no-object-em rule recognizes a fully documented object as this intentional documentation form.

Extract member views once and reference members through the extracted constant:

const  = .;

.; // preferred
..; // lint error

The same convention applies to .rawEnum and .cases. It makes the owning vocabulary visually stable and prevents instance expressions from leaking throughout application code. Other instance APIs such as roles.parse(...), roles.derive(...), and roles.values remain direct APIs.

.values: branded iteration

Use .values when iterating over owned application members:

for (const  of .) {
  ();
}

The tuple carries declaration provenance. Do not pass it back into em() to reconstruct another declaration; use pick, omit, extend, or em.combine to preserve the intended identity relationship.

.rawEnum and .rawValues: integration escapes

Some APIs require literal strings or literal arrays and cannot preserve enumwaii's brand. .rawEnum and .rawValues provide canonical unbranded values without making raw literals the normal authoring style. With an object declaration, .rawEnum retains the supplied developer-facing keys while .rawValues contains only the mapped values.

const  = .;

.({ : . });
.("role", .);

Keep these surfaces at the integration boundary, then parse values returning from that boundary to establish application ownership.

.cases: native discriminated-union narrowing

TypeScript does not reliably narrow discriminated unions when the discriminant is a branded string intersection. .cases supplies raw literal tags for union definitions, switch, and equality narrowing:

const  = (["CREATED", "DELETED"]);
const  = .;

type  =
  | { : typeof .; : <string, unknown> }
  | { : typeof .; : string };

function (: ) {
  switch (.) {
    case .:
      .; // correctly narrowed
      break;
    case .:
      .; // correctly narrowed
  }
}

.cases is not a second general-purpose enum. Treat .enum as the application-value surface and .cases as the narrow compatibility surface for discriminated unions. The .cases object carries a type marker so enumwaii's lint rules can recognize its provenance and keep its members in union definitions and narrowing positions.

Runtime identity and object behavior

.enum, .rawEnum, and .cases are different static views of one canonical frozen object:

.(., .); // true
.(., .); // true

They are frozen plain objects rather than proxies. Unknown properties therefore return undefined, which lets React, serializers, assertion libraries, inspectors, and promise detection probe them without triggering member-access errors.

The distinction between the surfaces exists entirely in TypeScript. Runtime code cannot recover which view a consumer used.

Type-only properties

Three declaration-local properties provide compile-time utilities. They are not emitted at runtime:

type  = (typeof )["~type"];
type  = (typeof )["~keys"];
type  = (typeof )["~safeParseResult"];
  • ~type is the branded member union.
  • ~keys is the raw value union, useful for external value-keyed records with satisfies. Despite its historical name, it does not become the object-overload's developer-facing property-key union.
  • ~safeParseResult is the exact discriminated result returned by this declaration's safeParse method.
const  = {
  : "Administrator",
  : "Member",
} as  satisfies <(typeof )["~keys"], string>;

Use derive instead when the record is part of enumwaii-controlled application behavior and source-member provenance matters.

On this page