Как проверить, существует ли значение в enum в TypeScript?
Проверка существования значения в enum в TypeScript
Проблема, с которой вы столкнулись, действительно типична. В TypeScript enum — это не просто набор констант, а специальная конструкция, которая создаёт как тип, так и объект. Для проверки существования значения нужно учитывать, работаете ли вы с числовыми или строковыми enum. Рассмотрим оба случая.
Для строковых enum (ваш случай):
enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest'
}
// Простая проверка через Object.values
function isUserRole(value: string): value is UserRole {
return Object.values(UserRole).includes(value as UserRole);
}
// Использование
const testValue = 'moderator';
if (isUserRole(testValue)) {
console.log('Допустимое значение');
} else {
console.log('Недопустимое значение'); // выведет это
}
Обратите внимание на value is UserRole — это TypeScript-гард, который сужает тип. Однако as UserRole внутри функции — это костыль. Лучше использовать более строгий подход:
function isUserRole(value: string): value is UserRole {
const values = Object.values(UserRole);
return (values as string[]).includes(value);
}
Для числовых enum:
enum Status {
Active = 1,
Inactive = 2,
Pending = 3
}
function isStatus(value: number): value is Status {
return Object.values(Status).includes(value);
}
// Но! Для числовых enum Object.values возвращает и ключи, и значения
// Поэтому лучше использовать фильтрацию:
function isStatusSafe(value: number): value is Status {
return (Object.values(Status) as number[]).includes(value);
}
Универсальное решение с помощью дженериков:
function isEnumValue<T extends Record<string, string | number>>(
enumObj: T,
value: unknown
): value is T[keyof T] {
const values = Object.values(enumObj);
return values.includes(value as T[keyof T]);
}
// Использование:
if (isEnumValue(UserRole, 'admin')) {
// value теперь имеет тип UserRole
}
Важные моменты:
- Для строковых enum — Object.values() возвращает только значения, всё чисто.
- Для числовых enum — Object.values() возвращает массив с дублированием (ключи и значения), поэтому нужна фильтрация.
- TypeScript гарды (value is Type) помогают сохранять типобезопасность.
- const enum не работают с Object.values() — они исчезают после компиляции.
Альтернатива с Map для производительности:
const userRoleValues = new Set(Object.values(UserRole));
function isUserRoleFast(value: string): value is UserRole {
return userRoleValues.has(value as UserRole);
}
Этот подход быстрее для частых проверок, так как Set использует хэш-таблицу.
<<Дополнительный подход: проверка через ключи enum
Иногда проще перевернуть задачу — проверять не значения, а ключи. Это особенно удобно, если ваши значения уникальны и вы хотите получить ключ по значению:
enum UserRole {
Admin = 'admin',
User = 'user',
Guest = 'guest'
}
// Создаём обратное отображение
const roleByValue = Object.fromEntries(
Object.entries(UserRole).map(([key, value]) => [value, key])
);
function getRoleKey(value: string): keyof typeof UserRole | undefined {
return roleByValue[value] as keyof typeof UserRole | undefined;
}
// Использование
const key = getRoleKey('admin'); // 'Admin'
if (key) {
console.log(`Роль: ${key}`);
}
Когда это полезно:
- Если вам нужно не просто проверить существование, но и получить ключ enum.
- При работе с внешними данными, где приходят строковые значения.
- Для создания обратных словарей в конфигурациях.
Недостаток: этот подход требует дополнительной памяти для хранения обратного отображения, но для небольших enum это не критично.
Ответов пока нет.
Войдите чтобы оставить ответ