enum-plus
English | 中文
Like native enum
, but much better than that!
![GitHub License](https://img.shields.io/github/license/shijistar/enum-plus?label=License&color=%23F68F1E)
⬇️ Introduction | Features | Installation | Enum Definition | API | Usage | Localization ⬇️
Introduction
enum-plus
is an enhanced enum library that is fully compatible with the basic usage of native enum
, while supporting extending display text, binding to UI components, and providing rich extension methods. It is a lightweight, zero-dependency, 100% TypeScript implementation tool that is suitable for a variety of front-end frameworks and supports localization.
After extending the display name of the enum item, it can be used to generate dropdowns, checkboxes, and other components with a single line of code. By using the extension methods of the enum, you can easily traverse the array of enum items, get the display text of a certain enum value, determine whether a value exists, etc. The display text of the enum item supports localization, which can return the corresponding text according to the current language environment, making the display text of the enum item more flexible and more in line with user needs.
What other exciting features are there? Please continue to explore the technical documentation below!
Features
- Fully compatible with native
enum
usage - Supports multiple data types such as
number
and string
- Support extending display text for enum items>
- Display text supports localization, you can use any internationalization library
- Support converting enum values to display text, making the code more concise
- Enum items support extending any number of custom fields
- Supports binding enums to AntDesign, ElementPlus, Material-UI or any other libraries, in a single line of code
- Zero dependencies, pure native JavaScript, can be applied to any front-end framework
- 100% TypeScript implementation, good support for type inference
- Lightweight (only 2KB+ gzipped)
Installation
Install using npm:
npm install enum-plus
Install using pnpm:
pnpm add enum-plus
Install using bun:
bun add enum-plus
Or using yarn:
yarn add enum-plus
Enum Definition
Create an enum, enum values support both number
and string
types
Example 1: Basic usage, almost the same as native enum
import { Enum } from 'enum-plus';
const Week = Enum({
Sunday: 0,
Monday: 1,
} as const);
Week.Monday;
Example 2: string value type
import { Enum } from 'enum-plus';
const Week = Enum({
Sunday: 'Sun',
Monday: 'Mon',
} as const);
Week.Monday;
👍👍 [Recommended] Example 3 (standard usage): With key, value, and display text
import { Enum } from 'enum-plus';
const Week = Enum({
Sunday: { value: 0, label: 'Sunday' },
Monday: { value: 1, label: 'Monday' },
} as const);
Week.Monday;
Week.label(1);
👍 Example 4: Omit the value field, automatically degrade to use the key field
import { Enum } from 'enum-plus';
const Week = Enum({
Sunday: { label: 'Sunday' },
Monday: { label: 'Monday' },
} as const);
Week.Monday;
Week.label('Monday');
Example 5: Create an enum dynamically
Sometimes we need to create an enum dynamically using data returned by an api, in this case, we can use an array to initialize the enum
```js
import { Enum } from 'enum-plus';
const petTypes = await getPetsData();
// [ { id: 1, code: 'dog', name: 'Dog' },
// { id: 2, code: 'cat', name: 'Cat' },
// { id: 3, code: 'rabbit', name: 'Rabbit' } ];
const PetTypes = Enum(petTypes, {
getValue: 'id',
getLabel: 'name',
getKey: 'code', // Optional, if omitted, value is used as Key as fallback
});
Week.values; // Output is:
// [ { value: 1, label: 'Dog', key: 'dog' },
// { value: 2, label: 'Cat', key: 'cat' },
// { value: 3, label: 'Rabbit', key: 'rabbit' } ]
API
Pick enum value
Enum.XXX
Like native enum
, pick a value of an enum item from the enum type
Week.Monday;
Week.Sunday;
values
{value, label, key, raw}[]
Get a read-only array containing all enum items, which can be easily traversed. Since it conforms to the data specification of AntDesign components, it supports one-click conversion of enums into components such as dropdowns and checkboxes, with just a single line of code. For more details, please refer to the examples below.
keys
string[]
Get a read-only array containing all Key
of the enum items
label
[Function] label(keyOrValue?: string | number): string | undefined
Get the display text of an enum item based on a certain enum value or Key. If localization is setup, the localized text will be returned.
Week.label(1);
Week.label('Monday');
key
[Function] key(value?: string | number): string | undefined
Get the Key of an enum item based on the enum value, if the Key is not found, return undefined
.
Week.key(1);
has
[Function] has(keyOrValue?: string | number): boolean
Determine whether a certain enum item (value or Key) exists
Week.has(1);
Week.has('Sunday');
Week.has(9);
Week.has('Birthday');
options
[Function] options(config?: OptionsConfig): {value, label}[]
options
is similar to values
, both return an array containing all enum items. The difference is that the elements returned by options
only contain the label
and value
fields. At the same time, the options
method supports inserting a default element at the beginning of the array, which is generally used for the default option of components such as dropdowns, representing all, none, or unlimited, etc., of course, you can also customize this default option
valuesEnum
[Function] valuesEnum(): Record<V, { text: string }>
Generate an enum collection object that conforms to the AntDesignPro specification, which can be passed to components like ProFormField
, ProTable
The data format is:
{
0: { text: 'Sunday' },
1: { text: 'Monday' },
}
filters
[Function] filters(): { text, value }[]
Generate an array of filters that can be passed directly to the Column.filters
of the AntDesign Table component as a list of filtered items for the column, displaying a dropdown filter box in the table header to filter table data
The data format is:
[
{ text: 'Sunday', value: 0 },
{ text: 'Monday', value: 1 },
];
raw
[Override^1] raw(): Record<K, T[K]>
[Override^2] raw(keyOrValue: V | K): T[K]
The first overload without parameters returns the initialization object of the enum collection, which is used to initialize the Enum original init object.
The second overload method is used to process a single enum item. Get the original initialization object of the enum item based on the enum value or enum Key, that is, the return value of the first method is part of the return value of the second method. In addition, if additional extension fields are added to the enum item, they can also be obtained in this way
const Week = Enum({
Sunday: { value: 0, label: 'Sunday' },
Monday: { value: 1, label: 'Monday' },
} as const);
Week.raw();
Week.raw(0);
Week.raw('Monday');
valueType [Type-ONLY]
value1 | value2 | ...
In TypeScript, get a union type containing all enum values, used to narrow the data type of variables or component properties, avoid using number
, string
and other overly broad types, improve code readability and type safety
const weekValue: typeof Week.valueType = 1;
const weeks: typeof Week.valueType[] = [0, 1];
type WeekValues = typeof Week.valueType;
Note that this is only a TypeScript type, which can only be used to constrain types and cannot be called at runtime, calling at runtime will throw an exception
keyType [Type-ONLY]
key1 | key2 | ...
Similar to valueType
, get a union type containing all enum Keys
const weekKey: typeof Week.keyType = 'Monday';
const weekKeys: typeof Week.keyType[] = ['Sunday', 'Monday'];
type WeekKeys = typeof Week.keyType;
Note that this is only a TypeScript type, which can only be used to constrain types and cannot be called at runtime, calling at runtime will throw an exception
rawType [Type-ONLY]
{ value: V, label: string, [...] }
Similar to the raw
method without parameters, but the raw
method supports runtime calls, while rawType
can only be used to constrain types
Note that this is only a TypeScript type, which can only be used to constrain types and cannot be called at runtime, calling at runtime will throw an exception
Usage
Access enum items, consistent with native enum usage
const Week = Enum({
Sunday: { value: 0, label: 'Sunday' },
Monday: { value: 1, label: 'Monday' },
} as const);
Week.Monday;
Week.Sunday;
In the code editor, hover over an enum item to display detailed Jsdoc comments about the enum item, without having to go back to the enum definition. In addition, when entering HttpCodes.
, the editor will automatically prompt the enum item list, switch enum items through the up and down keys, and also display detailed information
const HttpCodes = Enum({
E400: { value: 400, label: 'Bad Request' },
E401: { value: 401, label: 'Unauthorized' },
E403: { value: 0, label: 'Forbidden' },
E404: { value: 1, label: 'Not Found' },
} as const);
HttpCodes.E404;
In the above code example, the interpretation of Http status codes is based on MDN
Get array of enum items
Week.values;
Get the value of the first enum item
Week.values[0].value;
Determine whether a certain value is included in the enum
Week.values.some(item => item.value === 1);
Week.has(1);
1 instance of Week;
instanceof
operator
1 instance of Week
"1" instance of Week
"Monday" instance of Week
Support traversing the array of enum items, but not modifying
Week.values.length;
Week.values.map((item) => item.value);
Week.values.forEach((item) => {});
for (let item of Week.values) {
}
Week.values.push({ value: 2, label: 'Tuesday' });
Week.values.splice(0, 1);
Week.values[0].label = 'foo';
Get the display text of a certain value
Week.label(1);
Week.label(Week.Monday);
Week.label('Monday');
Get the key of a certain enum item
Week.key(1);
Week.key(Week.Monday);
Week.key(9);
Add custom fields
const Week = Enum({
Sunday: { value: 0, label: 'Sunday', active: true, disabled: false },
Monday: { value: 1, label: 'Monday', active: false, disabled: true },
} as const);
Week.raw(0).active
Week.raw(Week.Sunday).active
Week.raw('Sunday').active
Convert to UI components
-
values
can be consumed as the data source (here uses Select as examples)
AntDesign Select
import { Select } from 'antd';
<Select options={Week.values} />;
Material-UI Select
import { Select, MenuItem } from '@mui/material';
<Select>
{Week.values.map((item) => (
<MenuItem key={item.value} value={item.value}>
{item.label}
</MenuItem>
))}
</Select>;
Kendo UI Select
import { DropDownList } from '@progress/kendo-react-dropdowns';
<DropDownList data={Week.values} textField="label" dataItemKey="value" />;
ElementPlus Select
<el-select>
<el-option v-for="item in Week.values" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
Vuetify Select
<v-select :items="Week.values" item-title="label" item-value="value" />
Angular Material Select
HTML
<mat-select>
<mat-option *ngFor="let item of Week.values" [value]="item.value">{{ item.label }}</mat-option>
</mat-select>
NG-ZORRO Select
HTML
<nz-select>
<nz-option *ngFor="let item of Week.values" [nzValue]="item.value">{{ item.label }}</nz-option>
</nz-select>
-
options
method is similar to values
, but is allowed to add a default option at the top. The default option can be a boolean value or a custom object.
- If set to a boolean value, the default option is
{ value: '', label: 'All' }
, the display name only supports English. If you need localization, please parse and process the built-in resource key enum-plus.options.all
in the localization method. For more details about localization, please refer to the Localization section - If set to an object, you can customize the value and display text of the default option, and the display text will automatically support localization
<Select options={Week.options({ firstOption: true })} />
<Select options={Week.options({ firstOption: { value: 0, label: 'Unlimited' } })} />
-
menus
method can generate data sources for AntDesign Menu
, Dropdown
components, the format is: { key: number|string, label: string }[]
import { Menu } from 'antd';
<Menu items={Week.menus()} />;
filters
method can generate data sources for the Column filters
feature of the AntDesign Table
component, the format is: { text: string, value: number|string }[]
import { Table } from 'antd';
const columns = [
{
title: 'week',
dataIndex: 'week',
filters: Week.filters(),
},
];
<Table columns={columns} />;
valuesEnum
method can generate data sources for ProFormFields
, ProTable
components of AntDesignPro, which is a data structure similar to Map
, the format is: { [key: number|string]: { text: string } }
import { ProTable } from '@ant-design/pro-components';
<ProFormSelect valueEnum={Week.valuesEnum()} />;
Merge two enums (or extend an enum)
const myWeek = Enum({
...Week.raw(),
Friday: { value: 5, label: 'Friday' },
Saturday: { value: 6, label: 'Saturday' },
});
Narrowing data types with enum value sequences [TypeScript ONLY]
By using the valueType
type constraint, you can narrow the field type from the broad number
or string
type to a limited sequence of enum values, which not only reduces the possibility of erroneous assignments, but also improves the readability of the code.
const weekValue: number = 8;
const weekName: string = 'Birthday';
const badWeekValue: typeof Week.valueType = 8;
const badWeekName: typeof Week.keyType = 'Birthday';
const goodWeekValue: typeof Week.valueType = 1;
const goodWeekName: typeof Week.keyType = 'Monday';
type FooProps = {
value?: typeof Week.valueType;
names?: typeof Week.keyType[];
};
😟 Naming conflict?
Here are some edge cases for using enums. As seen from the above examples, we can quickly access enum items through Week.XXX
, but what if the key of an enum item conflicts with the name of an enum method?
We know that there are methods like label
, key
, options
on the enum type. If they have the same name as an enum item, the enum item's value has a higher priority and will override these methods. But don't worry, you can access them under values
. Please refer to the code example below:
const Week = Enum({
foo: { value: 1 },
bar: { value: 2 },
keys: { value: 3 },
label: { value: 4 },
} as const);
Week.keys;
Week.label;
Week.values.keys
Week.values.label(1);
An even more extreme case, what if values
conflicts with the name of an enum item? Don't worry, you can still access the values
array through an alias field. Refer to the example below:
import { VALUES } from 'enum-plus';
const Week = Enum({
foo: { value: 1 },
bar: { value: 2 },
values: { value: 3 },
} as const);
Week.values;
Week[VALUES];
Localization
enum-plus
does not provide internationalization functionality itself, but supports custom localization methods through the localize
optional parameter. You can declare a localization method in your project to convert the input enum label
into the corresponding localized text. You need to maintain the language yourself and return the corresponding text for the current language in the localize
method. If possible, it is strongly recommended that you use a popular internationalization library, such as i18next
Here is a simple example, but the first method is not a good practice because it is not flexible enough, and is only used to demonstrate basic functionality
import { Enum } from 'enum-plus';
import i18next from 'i18next';
import Localize from './Localize';
let lang = 'zh-CN';
const setLang = (l: string) => {
lang = l;
};
const sillyLocalize = (content: string) => {
if (lang === 'zh-CN') {
switch (content) {
case 'enum-plus.options.all':
return '全部';
case 'week.sunday':
return '星期日';
case 'week.monday':
return '星期一';
default:
return content;
}
} else {
switch (content) {
case 'enum-plus.options.all':
return 'All';
case 'week.sunday':
return 'Sunday';
case 'week.monday':
return 'Monday';
default:
return content;
}
}
};
const i18nLocalize = (content: string | undefined) => i18next.t(content);
const componentLocalize = (content: string | undefined) => <Localize value={content} />;
const Week = Enum(
{
Sunday: { value: 0, label: 'week.sunday' },
Monday: { value: 1, label: 'week.monday' },
} as const,
{
localize: sillyLocalize,
}
);
setLang('zh-CN');
Week.label(1);
setLang('en-US');
Week.label(1);
Setting each enum type individually can be cumbersome. You can also set localization globally using the Enum.localize
method. If both static settings and initialization options are provided, the initialization options take precedence.
Enum.localize = sillyLocalize;