Как преобразовать TypeScript enum в массив объектов для отображения в UI?
Проблема с дублированием в enum
Вы столкнулись с особенностью TypeScript enum. Когда enum содержит только строковые значения, TypeScript создает обратную маппинг-запись: для каждого ключа создается пара (ключ -> значение) и (значение -> ключ). Именно поэтому Object.entries() возвращает дублирующиеся записи.
Решение: фильтрация числовых ключей
Самый надежный способ — отфильтровать только строковые ключи, так как настоящие ключи enum всегда являются строками (не числами). Вот универсальное решение:
enum OrderStatus {
Pending = 'pending',
Processing = 'processing',
Shipped = 'shipped',
Delivered = 'delivered',
Cancelled = 'cancelled'
}
function enumToArray<T extends Record<string, string | number>>(enumObj: T): { label: string; value: T[keyof T] }[] {
return Object.keys(enumObj)
.filter(key => isNaN(Number(key))) // отсеиваем числовые ключи (обратная маппинг)
.map(key => ({
label: key,
value: enumObj[key] as T[keyof T]
}));
}
const statusArray = enumToArray(OrderStatus);
// Результат:
// [
// { label: 'Pending', value: 'pending' },
// { label: 'Processing', value: 'processing' },
// { label: 'Shipped', value: 'shipped' },
// { label: 'Delivered', value: 'delivered' },
// { label: 'Cancelled', value: 'cancelled' }
// ]
Альтернативный подход: использование Object.values() с фильтрацией
Можно использовать комбинацию Object.values() и Object.keys():
function enumToArraySimple(enumObj: any): { label: string; value: string }[] {
const keys = Object.keys(enumObj).filter(key => isNaN(Number(key)));
return keys.map(key => ({
label: key,
value: enumObj[key]
}));
}
Для числовых enum
Если ваш enum использует числовые значения, решение будет другим:
enum NumericStatus {
Pending = 0,
Processing = 1,
Shipped = 2
}
function numericEnumToArray(enumObj: any): { label: string; value: number }[] {
const keys = Object.keys(enumObj).filter(key => isNaN(Number(key)));
return keys.map(key => ({
label: key,
value: enumObj[key] as number
}));
}
const numericArray = numericEnumToArray(NumericStatus);
// Результат:
// [
// { label: 'Pending', value: 0 },
// { label: 'Processing', value: 1 },
// { label: 'Shipped', value: 2 }
// ]
Универсальный вариант с TypeScript generics
Вот полностью типизированное решение, которое работает с любыми enum:
type EnumType = Record<string, string | number>;
function enumToObjectArray<T extends EnumType>(
enumObj: T
): Array<{ label: keyof T; value: T[keyof T] }> {
return (Object.keys(enumObj) as Array<keyof T>)
.filter(key => isNaN(Number(key)))
.map(key => ({
label: key,
value: enumObj[key]
}));
}
// Использование:
const result = enumToObjectArray(OrderStatus);
// TypeScript корректно выведет тип:
// Array<{ label: "Pending" | "Processing" | "Shipped" | "Delivered" | "Cancelled"; value: string }>
Рекомендации
- Всегда используйте
isNaN(Number(key))для фильтрации обратной маппинг-записей - Для строковых enum достаточно простой фильтрации
- Для числовых enum проверка
isNaNтакже работает корректно - Если вы используете const enum (с модификатором
const), такой подход не сработает — const enum не имеют runtime-представления
Этот метод гарантированно избавит вас от дублирующихся записей и даст чистый массив объектов, готовый для использования в UI-компонентах.
<<Простое решение через reduce
Можно использовать более лаконичный подход с методом reduce, который сразу фильтрует и преобразует enum:
enum OrderStatus {
Pending = 'pending',
Processing = 'processing',
Shipped = 'shipped',
Delivered = 'delivered',
Cancelled = 'cancelled'
}
const statusArray = Object.keys(OrderStatus)
.reduce((acc, key) => {
if (isNaN(Number(key))) {
acc.push({ label: key, value: OrderStatus[key as keyof typeof OrderStatus] });
}
return acc;
}, [] as { label: string; value: string }[]);
console.log(statusArray);
// Результат: правильный массив без дубликатов
Использование сторонней библиотеки
Если вы часто работаете с enum и не хотите писать утилиты вручную, можно использовать библиотеку ts-enum-util:
import { $enum } from 'ts-enum-util';
const statusArray = $enum(OrderStatus)
.getKeys()
.map(key => ({
label: key,
value: $enum(OrderStatus).getValue(key)
}));
Этот подход особенно удобен, если вам нужно часто преобразовывать разные enum в массивы для select-компонентов. Библиотека также поддерживает строгую типизацию и работает с числовыми enum.
Ответов пока нет.
Войдите чтобы оставить ответ