Home » Как использовать перечисления (Enums) в TypeScript
Как использовать перечисления (Enums) в TypeScript

Как использовать перечисления (Enums) в TypeScript

Когда разрабатываешь серверные приложения на TypeScript, часто сталкиваешься с необходимостью работать с фиксированными наборами значений — статусы запросов, типы пользователей, коды ошибок HTTP. Именно здесь на помощь приходят перечисления (Enums), которые позволяют сделать код более читаемым, типобезопасным и менее подверженным ошибкам. Если ты работаешь с REST API, настраиваешь мониторинг сервера или пишешь скрипты автоматизации, то правильное использование Enums может значительно упростить твою жизнь и сделать код более maintainable.

Как работают перечисления в TypeScript

Enums в TypeScript — это способ задать набор именованных констант, которые могут быть как числовыми, так и строковыми. В отличие от обычных JavaScript-констант, Enums предоставляют дополнительные возможности типизации и удобство работы.

Базовый синтаксис выглядит так:

enum ServerStatus {
  Online,
  Offline,
  Maintenance,
  Error
}

// Использование
const currentStatus = ServerStatus.Online;
console.log(currentStatus); // 0 (числовое значение)
console.log(ServerStatus[0]); // "Online" (обратное получение имени)

По умолчанию TypeScript присваивает числовые значения начиная с 0, но можно задать кастомные значения:

enum HttpStatus {
  OK = 200,
  NotFound = 404,
  InternalError = 500
}

enum LogLevel {
  DEBUG = "debug",
  INFO = "info",
  WARNING = "warning",
  ERROR = "error"
}

Пошаговая настройка и примеры использования

Давайте разберём практический пример создания серверного приложения с использованием Enums:

Шаг 1: Инициализация проекта

mkdir server-enums-example
cd server-enums-example
npm init -y
npm install typescript @types/node ts-node
npx tsc --init

Шаг 2: Создание базовых перечислений

Создай файл enums.ts с основными перечислениями для серверного приложения:

// enums.ts
export enum ServerEnvironment {
  DEVELOPMENT = "development",
  STAGING = "staging",
  PRODUCTION = "production"
}

export enum DatabaseStatus {
  CONNECTED = "connected",
  DISCONNECTED = "disconnected",
  CONNECTING = "connecting",
  ERROR = "error"
}

export enum UserRole {
  ADMIN = "admin",
  MODERATOR = "moderator",
  USER = "user",
  GUEST = "guest"
}

export enum ApiVersion {
  V1 = "v1",
  V2 = "v2",
  V3 = "v3"
}

// Числовые Enums для приоритетов
export enum Priority {
  LOW = 1,
  MEDIUM = 2,
  HIGH = 3,
  CRITICAL = 4
}

Шаг 3: Практическое применение в серверном коде

// server.ts
import { ServerEnvironment, DatabaseStatus, UserRole, Priority } from './enums';

class ServerManager {
  private environment: ServerEnvironment;
  private dbStatus: DatabaseStatus;
  
  constructor(env: ServerEnvironment) {
    this.environment = env;
    this.dbStatus = DatabaseStatus.DISCONNECTED;
  }
  
  // Метод для проверки доступа на основе роли
  checkAccess(userRole: UserRole, requiredRole: UserRole): boolean {
    const roleHierarchy = {
      [UserRole.GUEST]: 0,
      [UserRole.USER]: 1,
      [UserRole.MODERATOR]: 2,
      [UserRole.ADMIN]: 3
    };
    
    return roleHierarchy[userRole] >= roleHierarchy[requiredRole];
  }
  
  // Логирование с приоритетом
  log(message: string, priority: Priority): void {
    const timestamp = new Date().toISOString();
    const envPrefix = this.environment.toUpperCase();
    
    if (priority >= Priority.HIGH) {
      console.error(`[${envPrefix}] ${timestamp} CRITICAL: ${message}`);
    } else {
      console.log(`[${envPrefix}] ${timestamp} ${Priority[priority]}: ${message}`);
    }
  }
}

// Использование
const server = new ServerManager(ServerEnvironment.PRODUCTION);
server.log("Server started", Priority.HIGH);
console.log(server.checkAccess(UserRole.MODERATOR, UserRole.USER)); // true

Типы перечислений и их особенности

Тип Enum Преимущества Недостатки Когда использовать
Числовые Компактные, быстрые операции сравнения Нечитаемые в логах, могут измениться при рефакторинге Внутренние флаги, приоритеты
Строковые Читаемые, стабильные значения Больше памяти, медленнее сравнение API responses, конфигурация
Const enum Встраиваются в код, нет runtime overhead Нельзя итерировать, проблемы с модулями Константы времени компиляции

Продвинутые техники и паттерны

Const Enums для оптимизации

// Const enum полностью встраивается в код при компиляции
const enum ResponseCode {
  SUCCESS = 200,
  NOT_FOUND = 404,
  SERVER_ERROR = 500
}

// Компилируется в: if (status === 200)
if (status === ResponseCode.SUCCESS) {
  // handle success
}

Reverse mapping для числовых enum

enum ProcessStatus {
  PENDING = 1,
  RUNNING = 2,
  COMPLETED = 3,
  FAILED = 4
}

// Получение имени по значению
function getStatusName(status: number): string {
  return ProcessStatus[status] || "Unknown";
}

console.log(getStatusName(2)); // "RUNNING"

Создание утилит для работы с Enums

// utils.ts
export class EnumUtils {
  static getValues>(enumObject: T): T[keyof T][] {
    return Object.values(enumObject);
  }
  
  static getKeys>(enumObject: T): string[] {
    return Object.keys(enumObject);
  }
  
  static isValidValue>(
    enumObject: T, 
    value: any
  ): value is T[keyof T] {
    return Object.values(enumObject).includes(value);
  }
}

// Использование
const validRoles = EnumUtils.getValues(UserRole);
const isValidRole = EnumUtils.isValidValue(UserRole, "admin"); // true

Интеграция с популярными библиотеками

Использование с Express.js

import express from 'express';
import { UserRole, ApiVersion } from './enums';

const app = express();

// Middleware для проверки версии API
app.use('/api/:version', (req, res, next) => {
  const version = req.params.version as ApiVersion;
  
  if (!EnumUtils.isValidValue(ApiVersion, version)) {
    return res.status(400).json({ error: 'Invalid API version' });
  }
  
  next();
});

// Защищённый роут с проверкой роли
app.get('/admin', (req, res) => {
  const userRole = req.user?.role as UserRole;
  
  if (userRole !== UserRole.ADMIN) {
    return res.status(403).json({ error: 'Access denied' });
  }
  
  res.json({ message: 'Admin panel access granted' });
});

Конфигурация с переменными окружения

// config.ts
import { ServerEnvironment } from './enums';

export class Config {
  static getEnvironment(): ServerEnvironment {
    const env = process.env.NODE_ENV;
    
    switch (env) {
      case 'development':
        return ServerEnvironment.DEVELOPMENT;
      case 'staging':
        return ServerEnvironment.STAGING;
      case 'production':
        return ServerEnvironment.PRODUCTION;
      default:
        return ServerEnvironment.DEVELOPMENT;
    }
  }
  
  static isDevelopment(): boolean {
    return this.getEnvironment() === ServerEnvironment.DEVELOPMENT;
  }
}

Автоматизация и скрипты

Enums отлично подходят для создания скриптов автоматизации серверных задач:

// deployment-script.ts
enum DeploymentStage {
  BUILD = "build",
  TEST = "test",
  DEPLOY = "deploy",
  VERIFY = "verify",
  ROLLBACK = "rollback"
}

class DeploymentManager {
  async executeStage(stage: DeploymentStage): Promise {
    console.log(`Executing stage: ${stage}`);
    
    switch (stage) {
      case DeploymentStage.BUILD:
        return this.buildApplication();
      case DeploymentStage.TEST:
        return this.runTests();
      case DeploymentStage.DEPLOY:
        return this.deployToServer();
      case DeploymentStage.VERIFY:
        return this.verifyDeployment();
      case DeploymentStage.ROLLBACK:
        return this.rollbackDeployment();
      default:
        throw new Error(`Unknown deployment stage: ${stage}`);
    }
  }
  
  private async buildApplication(): Promise {
    // Логика сборки
    return true;
  }
  
  // Другие методы...
}

// Использование в скрипте
const deployment = new DeploymentManager();
const stages = [
  DeploymentStage.BUILD,
  DeploymentStage.TEST,
  DeploymentStage.DEPLOY,
  DeploymentStage.VERIFY
];

stages.forEach(async (stage) => {
  const success = await deployment.executeStage(stage);
  if (!success) {
    await deployment.executeStage(DeploymentStage.ROLLBACK);
    process.exit(1);
  }
});

Мониторинг и метрики

// monitoring.ts
enum MetricType {
  CPU_USAGE = "cpu_usage",
  MEMORY_USAGE = "memory_usage",
  DISK_USAGE = "disk_usage",
  NETWORK_IO = "network_io"
}

enum AlertLevel {
  INFO = 1,
  WARNING = 2,
  ERROR = 3,
  CRITICAL = 4
}

class MonitoringSystem {
  private metrics: Map = new Map();
  
  updateMetric(type: MetricType, value: number): void {
    this.metrics.set(type, value);
    this.checkAlerts(type, value);
  }
  
  private checkAlerts(type: MetricType, value: number): void {
    let alertLevel: AlertLevel;
    
    switch (type) {
      case MetricType.CPU_USAGE:
        if (value > 90) alertLevel = AlertLevel.CRITICAL;
        else if (value > 70) alertLevel = AlertLevel.WARNING;
        else alertLevel = AlertLevel.INFO;
        break;
      
      case MetricType.MEMORY_USAGE:
        if (value > 85) alertLevel = AlertLevel.ERROR;
        else if (value > 60) alertLevel = AlertLevel.WARNING;
        else alertLevel = AlertLevel.INFO;
        break;
      
      default:
        alertLevel = AlertLevel.INFO;
    }
    
    if (alertLevel >= AlertLevel.WARNING) {
      this.sendAlert(type, value, alertLevel);
    }
  }
  
  private sendAlert(type: MetricType, value: number, level: AlertLevel): void {
    console.log(`ALERT [${AlertLevel[level]}]: ${type} = ${value}%`);
  }
}

Интересные факты и нестандартные применения

Несколько полезных трюков, которые могут пригодиться в работе:

  • Битовые флаги: Можно использовать числовые Enums для создания битовых флагов
  • Enum как тип Union: TypeScript автоматически создаёт Union type из значений Enum
  • Computed enum values: Можно использовать вычисляемые значения в Enums
// Битовые флаги
enum Permissions {
  READ = 1,
  WRITE = 2,
  EXECUTE = 4,
  DELETE = 8
}

const userPermissions = Permissions.READ | Permissions.WRITE;
const hasWriteAccess = (userPermissions & Permissions.WRITE) === Permissions.WRITE;

// Computed values
enum TimeConstants {
  SECOND = 1000,
  MINUTE = SECOND * 60,
  HOUR = MINUTE * 60,
  DAY = HOUR * 24
}

Лучшие практики и рекомендации

Основываясь на опыте работы с серверными приложениями, вот несколько важных рекомендаций:

  • Используй строковые Enums для API: Они более читаемы в логах и стабильны при рефакторинге
  • Избегай смешивания типов: Не используй одновременно числовые и строковые значения в одном Enum
  • Создавай утилиты для валидации: Это поможет при работе с внешними данными
  • Используй const enum для performance-critical кода: Но помни об ограничениях
  • Именуй Enums в единственном числе: UserRole вместо UserRoles

Для серверных приложений, особенно если планируешь деплой на VPS или выделенном сервере, правильное использование Enums может значительно упростить мониторинг, логирование и обслуживание.

Заключение и рекомендации

Enums в TypeScript — это мощный инструмент для создания maintainable серверного кода. Они особенно полезны при работе с конфигурацией, статусами, ролями пользователей и другими фиксированными наборами значений. Использование Enums делает код более читаемым, менее подверженным ошибкам и упрощает рефакторинг.

Для серверных приложений рекомендую:

  • Использовать строковые Enums для внешних API и конфигурации
  • Числовые Enums подходят для внутренних флагов и приоритетов
  • Создавать утилиты для валидации и работы с Enums
  • Применять Enums в системах мониторинга и алертинга
  • Использовать их для создания типобезопасных скриптов автоматизации

Правильное применение Enums поможет создать более robust и maintainable серверные приложения, что особенно важно при работе в продакшене.


В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.

Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.

Leave a reply

Your email address will not be published. Required fields are marked