ts-create-enum
TypeScript icon, indicating that this package has built-in type declarations

1.0.3 • Public • Published

ts-create-enum

A utility for making type-safe, ordered, set-oriented enums in TypeScript. This package primarily contains two functions, CreateEnum and EnumOneToOneMapper.

CreateEnum

CreateEnum is a function that is used to generate an enum and corresponding type-safe utilities from a configuration.

const { Enum: Color } = CreateEnum(
  { Red: 'r' },
  { Green: 'g' },
  { Blue: 'b' },
);

const color = Color.Red; // 'r'

These utilities include an ordered list of elements, a type-guard, a mapping from each value to its index in the ordered list, and functions for making subsets and reorderings. The major advantage of this approach is its type-safety; the type of every value returned is known exactly at compile-time.

const {
  Enum,
  List,
  CreateOrdering,
  CreateSubset,
  CreateComplementSubset,
  TypeGuard,
  IndexByValue,
} = CreateEnum(
  { Red: 'r' },
  { Green: 'g' },
  { Blue: 'b' },
);

TypeScript enums can have inferred values, their order and number of entries aren't known to the type system, and number-based enums have the peculiar property where their keys are values and their values are also keys. These utilities are meant to allow for type-safe enums that avoid these issues and manage common operations without code duplication.

CreateSubset & CreateComplementSubset

The CreateSubset & CreateComplementSubset functions can be used in a type-safe way to declare relationships between different values of an enum:

const {
  Enum: LivingBeing,
  CreateSubset: CreateLivingBeingSubset,
  CreateComplementSubset: CreateLivingBeingComplementSubset
} = CreateEnum(
  { Wolf: 'wolf' },
  { Bear: 'bear' },
  { Jellyfish: 'jellyfish' },
  { Lion: 'lion' },
  { Tiger: 'tiger' },
  { Tree: 'tree' },
  { Grass: 'grass' },
  { Mushroom: 'mushroom' }
);

const { List: Plants } = CreateLivingBeingSubset(LivingBeing.Tree, LivingBeing.Grass);

const {
  CreateSubset: CreateNonPlantSubset,
  CreateComplementSubset: CreateNonPlantComplementSubset
} = CreateLivingBeingComplementSubset(...Plants);

const { List: Fungi } = CreateNonPlantSubset(LivingBeing.Mushroom);

const {
  Enum: Animal,
  CreateSubset: CreateAnimalSubset,
} = CreateNonPlantComplementSubset(...Fungi);

const {
  Enum: Mammal,
  List: Mammals,
  CreateSubset: CreateMammalSubset,
  CreateComplementSubset: CreateMammalComplementSubset
} = CreateAnimalSubset(Animal.Wolf, Animal.Bear, Animal.Lion, Animal.Tiger);

const { List: Cats } = CreateMammalSubset(Mammal.Lion, Mammal.Tiger);

const { List: NonCatMammals, IndexByValue: NonCatMammalsIndexByValue } = CreateMammalComplementSubset(...Cats);

// Each of the below has a type that exactly matches its value!

Plants;                    // ['tree', 'grass']
Mammals;                   // ['wolf', 'bear', 'lion', 'tiger']
NonCatMammals;             // ['wolf', 'bear']
NonCatMammalsIndexByValue; // { wolf: 0, bear: 1 }

CreateOrdering

The CreateOrdering is the same as CreateSubset except you must specify all elements of the enum, but you can specify them in a new order.

const {
  Enum: Color,
  CreateOrdering: CreateColorOrdering
} = CreateEnum(
  { Red: 'r' },
  { Green: 'g' },
  { Yellow: 'y' },
  { Blue: 'b' },
  { Purple: 'p' },
  { Orange: 'o' }
)

const { List: PrimarySecondaryColors } = CreateColorOrdering(
  Color.Red,
  Color.Yellow,
  Color.Blue,
  Color.Orange,
  Color.Green,
  Color.Purple
);

const { List: HueColors } = CreateColorOrdering(
  Color.Red,
  Color.Orange,
  Color.Yellow,
  Color.Green,
  Color.Blue,
  Color.Purple
);

TypeGuard

The TypeGuard method ensures that any value is a part of the enum or subset it is derived from.

const { TypeGuard: isColor } = CreateEnum(
  { Red: 'r' },
  { Green: 'g' },
  { Blue: 'b' },
);

const fn = (x: any) => {
  if(isColor(x)) {
    // x is 'r' | 'g' | 'b'
  }
}

Other Information

Declaring multiple aliases for the same value is supported as long as they are in the same object as other names with the same value. Additionally, any combination of string, number, and symbol values are allowed.

const blueSymbol = Symbol('blue');

const { Enum: Color } = CreateEnum(
  { Red: 'r', R: 'r', red: 'r' },
  { Green: 2 },
  { Blue: blueSymbol },
);

It is recommended that the result is destructured and its properties renamed to match your enum. As in these examples, the Enum property is best given as a singular form, while List is its plural form.

It is also recommended to export a type containing the union of values in your enum under the same name, as is done automatically for TypeScript's enums.

const {
  Enum: Color,
  List: Colors
} = CreateEnum(...);

type Color = (typeof Colors)[number];

function getRandomColor(): Color { ... }

Each subset will output a new Enum property which will use the same names and values as in the set that it is derived from, only it will be filtered down to the values allowed by that subset. This allows for subsets to also be named, as below.

const { Enum: LivingBeing } = CreateEnum(
  { Wolf: 'wolf' },
  { Bear: 'bear' },
  { Tree: 'tree' },
  { Grass: 'grass' },
);

const { Enum: Plant, List: Plants } = CreateLivingBeingSubset(LivingBeing.Tree, LivingBeing.Grass);

LivingBeing.Tree === Plant.Tree; // Plant.Tree exists because it is a part of the Plant subset
LivingBeing.Wolf !== Plant.Wolf; // Plant.Wolf does not exist!

EnumOneToOneMapper

EnumOneToOneMapper is a curried function that ensures exact 1:1 matching between two enums. It can be called as follows to retrieve the validated mapping and its inverse.

const {
  Enum: BodyPart,
  List: BodyParts
} = CreateEnum(
  { Head: 0 },
  { Feet: 1 },
  { Hands: 2 }
);

const {
  Enum: ClothingItem,
  List: ClothingItems
} = CreateEnum(
  { Boots: 'boots' },
  { Gloves: 'gloves' },
  { Hat: 'hat' }
);

const {
  Mapping: ClothingItemByBodyPart,
  InverseMapping: BodyPartByClothingItem
} = EnumOneToOneMapper(BodyParts, ClothingItems).Create({
  [BodyPart.Head]: ClothingItem.Hat,
  [BodyPart.Feet]: ClothingItem.Boots,
  [BodyPart.Hands]: ClothingItem.Gloves,
});

// Each of the below has a type that exactly matches its value!

ClothingItemByBodyPart; // { 0: 'hat', 1: 'boots', 2: 'gloves' }
BodyPartByClothingItem; // { hat: 0, boots: 1, gloves: 2 }

Package Sidebar

Install

npm i ts-create-enum

Weekly Downloads

6

Version

1.0.3

License

none

Unpacked Size

53.6 kB

Total Files

13

Last publish

Collaborators

  • connorullmann