Skip to content

Enum mapper

The enum mapper is a small utility for converting values between two related TypeScript enums in a strict and explicit way.

It is designed for a common situation:

  • one enum is used at an API or boundary layer
  • another enum is used internally
  • enum values are related but not identical
  • naming often differs by a suffix or convention

Instead of relying on string casts or as unknown as, the enum mapper enforces a total, reversible mapping.

Basic usage

ts
import { enumMapper } from "mapia/enum-mapper";

enum ApiRole {
  VIEWER = "VIEWER",
  READER = "READER",
}

enum InternalRole {
  VIEWER_ENUM = "VIEWER_ENUM",
  READER_ENUM = "READER_ENUM",
}

const roleMap = enumMapper(ApiRole, InternalRole, {
  VIEWER: InternalRole.VIEWER_ENUM,
  READER: InternalRole.READER_ENUM,
});

The returned mapper exposes two functions:

ts
roleMap.toDestination(ApiRole.VIEWER);
// → InternalRole.VIEWER_ENUM

roleMap.toSource(InternalRole.READER_ENUM);
// → ApiRole.READER

Both directions are type-safe.

Why this exists

TypeScript enums are nominally unrelated, even if their values look compatible.

This is unsafe but common:

ts
const internal = apiValue as unknown as InternalEnum;

Problems with this approach:

  • no guarantee that every enum member is mapped
  • no protection against extra or missing values
  • refactors silently break behavior
  • reverse mapping is usually forgotten

The enum mapper addresses these issues by requiring an explicit correspondence.

Custom suffixes

In many systems, enum values follow a predictable naming rule.

The enum mapper supports suffix-based conventions while preserving inference.

ts
enum HttpStatus {
  OK = "OK",
  NOT_OK = "NOT_OK",
}

enum HttpStatusCode {
  OK_CUSTOM = "OK_CUSTOM",
  NOT_OK_CUSTOM = "NOT_OK_CUSTOM",
}

const statusMap = enumMapper(
  HttpStatus,
  HttpStatusCode,
  {
    OK: HttpStatusCode.OK_CUSTOM,
    NOT_OK: HttpStatusCode.NOT_OK_CUSTOM,
  },
  "_CUSTOM",
);

The suffix is used internally to validate reverse mappings.

Compile-time validation

The enum mapper performs several checks at the type level.

Missing mappings

If a source enum value is not mapped, TypeScript reports an error.

Extra destination keys

If the destination enum contains values that cannot be matched back to the source enum (based on the suffix rule), the mapping resolves to never.

Example:

ts
type Check = EnumAutoSuffixMapping<
  typeof ApiEnum,
  typeof InternalEnumWithExtra,
  "_CUSTOM"
>;
// => never

This prevents silent drift between enums.

Released under the MIT License