@ivadey/enum-factory v1.0.6
Enum Factory – is a small, zero-dependency library for creating flexible, extensible and type-safe class-based enums in TypeScript. It overcomes the limitations of native enums and adds features like extensibility, strict type safety.
TypeScript enums are nice, but they have some problems:
- No way to detect that it's an enum – neither as a type nor at runtime
- No way to just extend enum and preserve typings
- Not typesafe in runtime
enum State {
Idle,
Running
}
enum Status {
New,
Saved
}
State.Idle === Status.New // get a TypeScript error, but true in runtime
Quick example
import EnumFactory from '@ivadey/enum-factory';
const State = EnumFactory.create('state', {
Idle: 0,
Running: 1
});
const Status = EnumFactory.create('states', {
New: 0,
Saved: 1
});
State.Idle === Status.New // get a TypeScript error and false in runtime
Installation
npm install @ivadey/enum-factory
# or
yarn add @ivadey/enum-factory
Features
Main advantages and features:
- Can be used in all cases in which can be used native typescript enums
- Extensible and type-safe enums even in runtime
- Class-based implementation for advanced functionality
- Works seamlessly with TypeScript
- Easy-to-use factory API
- Compatible with
Object.keys()
,Object.values()
andObject.entries
in additional to own methods - Serializable
- Available for work with primitives:
import EnumFactory from '@ivadey/enum-factory';
const Status = EnumFactory.create('states', {
New: 0,
Saved: 1
});
State.Idle === 0 // => false
State.Idle == 0 // => true
State.Idle === State.fromValue(0) // => true
Drawbacks
According to how typescript processing types some extra definitions required for some cases:
- Type must be explicitly defined to be used:
import EnumFactory, { EnumType } from '@ivadey/enum-factory';
const Status = EnumFactory.create('states', {
New: 0,
Saved: 1
});
type Status = EnumType<typeof Status>; // required to use Status as type
const status: Status = Status.New; // ok
- Keys types must be explicitly defined to be used as index signature
import EnumFactory, { EnumKeysType } from '@ivadey/enum-factory';
const Status = EnumFactory.create('states', {
New: 0,
Saved: 1
} as const); // defintion must be marked as const to resolve literal values
const statusNames: { [key in EnumKeysType<Status>]: string; } = {
[Status.New]: 'Draft',
[Status.Saved]: 'Saved',
foo: 'bar', // get a TypeScript error as 'foo' is not part of Status enum
}
- Although most of the time it works fine, in some cases need to explicitly call valueOf/toString/toJSON. For example console.log will print not that you may expect (as it's not part of EcmaScript):
import EnumFactory, { EnumKeysType } from '@ivadey/enum-factory';
const SomeEnum = EnumFactory.create('some-enum', { Foo: 'bar', Num: 12 } as const);
console.log(SomeEnum.Foo) // => Enum { [Symbol(some-enum)]: 'some-enum' }
console.log(SomeEnum.Foo.valueOf()) // => 'bar'
console.log(SomeEnum.Num.valueOf()) // => 12
console.log(SomeEnum.Num.toString()) // => '12'
Usage
import EnumFactory, { EnumType, EnumKeysType } from '@ivadey/enum-factory';
const State = EnumFactory.create('state', {
Idle: 'idle',
Active: 'active',
} as const);
type State = EnumType<typeof State>;
const print = (state: State) => {
switch (state) {
case State.Idle: console.log(State.Idle.valueOf()); break;
case State.Active: console.log(State.Active.valueOf()); break;
default: console.log('Unknown state');
}
};
print(State.Idle); // => 'idle'
print(State.Active); // => 'active'
console.log(State.keys()); // => ['Idle', 'Active']
console.log(Object.keys(State)); // => ['Idle', 'Active']
const obj: { [key in EnumKeysType<typeof State>]: string } = {
[State.Idle]: '', // ok
[State.Active]: '', // ok
idle: '', // ok
active: '', // ok
unknown: '', // not ok, getting a TypeScript error
};
API Documentation
EnumFactory
The EnumFactory class is the main entry point for creating advanced enumerations with extended functionality. Below is a breakdown of its methods and their purpose:
EnumFactory.create
Signature:
static create<TDefinition extends EnumDefinition, TEnumName extends string>(
name: TEnumName,
definition: TDefinition
): ClassEnum<TDefinition, keyof TDefinition, TEnumName>
Description:
Creates a new enumeration with the given name and definition.
- Ensures all enum values are unique.
- Prevents redefinition of an enum with the same name.
Parameters:
- name (string): The unique name of the enumeration.
- definition (object): Key-value pairs where keys are enum members and values are strings or numbers.
Returns:
A class representing the created enum with various utility methods.
Example:
const Colors = EnumFactory.create('Colors', {
RED: 'red',
GREEN: 'green',
BLUE: 'blue',
});
Enum Methods
Enums created via EnumFactory include the following static methods:
.fromValue(value)
Retrieves an enum member by its value. Will return undefined if given value is not part of the enum.
Example:
const red = Colors.fromValue('red');
.values()
Returns all enum members as an array.
Example:
const allColors = Colors.values(); // [Colors.RED, Colors.GREEN, Colors.BLUE]
.keys()
Returns all enum keys as an array.
Example:
const colorKeys = Colors.keys(); // ['RED', 'GREEN', 'BLUE']
.entries()
Returns an array of key, value pairs for the enum.
Example:
const colorEntries = Colors.entries(); // [['RED', Colors.RED], ['GREEN', Colors.GREEN], ...]
.cloneAndExtend(name, definition)
Creates a new enumeration by extending the current enum with additional members.
Example:
const ExtendedColors = Colors.cloneAndExtend('ExtendedColors', {
YELLOW: 'yellow',
});
Enum Member Methods
Enum members have the following methods:
.valueOf()
Returns the value associated with the enum member.
Example:
Colors.RED.valueOf(); // 'red'
Note: return type will be either literal, either value type depends on definition was passed as const or not:
const WithLiteralValue = EnumFactory.create('with-literal', { Red: 'red' } as const);
const WithJustTypeValue = EnumFactory.create('with-type', { Red: 'red' });
ReturnType<typeof WithLiteralValue.valueOf> // 'red'
ReturnType<typeof WithJustTypeValue.valueOf> // string
.toString()
Returns the value of the enum member as a string.
Example:
Colors.RED.toString(); // 'red'
.toJSON()
Returns the JSON representation of the enum member (its value).
Example:
JSON.stringify(Colors.RED); // '"red"'
Type Utilities
EnumFactory provides TypeScript utilities for working with enums:
EnumType
Extracts the type of all enum members.
Example:
type ColorsType = EnumType<typeof Colors>; // Colors.RED | Colors.GREEN | Colors.BLUE
EnumKeysType
Extracts a union type of valid keys for index signatures, including both enum member references and their values.
Example:
type ColorsKeysType = EnumKeysType<typeof Colors>; // 'red' | 'green' | 'blue'
Notes
- Enum values must be unique.
- Attempting to create an enum with the same name will throw an error.
- Once initialized, no new enum members can be added without using cloneAndExtend.
Contributing
Contributions are welcome! Please see the CONTRIBUTING.md file for details.
License
This project is licensed under the MIT License - see the LICENSE file for details.