Константы в TypeScript: как объявить и использовать в классах

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Константы в классах TypeScript: от новичка до профессионала

Вы когда-нибудь писали на TypeScript и вдруг осознавали: «Эту магическую строку "ADMIN" я использую в десяти местах, а если опечатаюсь — компилятор промолчит»? Знакомая боль. Или, что ещё хуже, кто-то из команды меняет значение константы прямо в рантайме, и вы проводите час за дебагом, чтобы понять, почему всё сломалось.

Добро пожаловать в мир констант классов в TypeScript. Это не просто синтаксический сахар — это фундамент чистой, предсказуемой и безопасной архитектуры. В этой статье мы разберём все способы объявления констант в TypeScript-классах: от простого readonly до продвинутых техник с as const и дженериками. Поехали!

Почему константы в классах — это важно?

Представьте, что вы разрабатываете приложение для интернет-магазина. У вас есть класс Order, и в нём есть статусы: "pending", "shipped", "delivered". Если вы разбросаете эти строки по всему коду, рано или поздно кто-то напишет "PENDING" с большой буквы — и баг обеспечен. Константы классов TypeScript решают эту проблему раз и навсегда.

  • Безопасность типов: компилятор не даст передать "pendingg" туда, где нужен "pending".
  • Читаемость: OrderStatus.Pending понятнее, чем просто строка.
  • Поддержка: изменение значения в одном месте меняет его везде.

Способ №1: readonly — база, но со нюансами

Самый простой способ — использовать модификатор readonly. Он запрещает изменение поля после инициализации.

class AppConfig {
    readonly API_URL: string = 'https://api.example.com';
    readonly MAX_RETRIES: number = 3;
}

const config = new AppConfig();
console.log(config.API_URL); // 'https://api.example.com'
// config.API_URL = 'new-url'; // Ошибка! Cannot assign to 'API_URL' because it is a read-only property.

Но есть подвох: readonly работает только на уровне TypeScript. В скомпилированном JavaScript эти поля всё ещё можно изменить. Для полной защиты используйте Object.freeze или # (приватные поля).

Способ №2: static readonly — король констант

Если константа не привязана к конкретному экземпляру класса (а так бывает в 99% случаев), используйте static readonly. Это классический class constant typescript паттерн.

class MathConstants {
    static readonly PI: number = 3.14159;
    static readonly E: number = 2.71828;
    static readonly GOLDEN_RATIO: number = 1.61803;
}

// Доступ без создания экземпляра
console.log(MathConstants.PI); // 3.14159
// MathConstants.PI = 3.14; // Ошибка!

Этот подход идеален для конфигураций, математических констант, статусов и любых значений, которые не должны меняться.

Способ №3: enum — когда констант много

Если у вас группа связанных констант (например, статусы заказа), enum — ваш лучший друг. Он создаёт именованное множество значений.

enum OrderStatus {
    Pending = 'pending',
    Shipped = 'shipped',
    Delivered = 'delivered',
    Cancelled = 'cancelled'
}

class Order {
    constructor(public status: OrderStatus) {}
}

const order = new Order(OrderStatus.Pending);
console.log(order.status); // 'pending'

// order.status = 'pendingg'; // Ошибка! Type '"pendingg"' is not assignable to type 'OrderStatus'

Совет: если вы используете строковые enum (как в примере), они дают читаемые значения в рантайме. Числовые enum быстрее, но их сложнее дебажить.

Способ №4: as const — для максимальной строгости

Иногда вы хотите, чтобы объект или массив стали константой с конкретными литеральными типами. Здесь на помощь приходит as const.

class HttpCodes {
    static readonly codes = {
        OK: 200,
        NotFound: 404,
        InternalError: 500
    } as const;
}

// Тип: 200 | 404 | 500
type HttpCode = typeof HttpCodes.codes[keyof typeof HttpCodes.codes];

function handleResponse(code: HttpCode) {
    if (code === HttpCodes.codes.OK) {
        console.log('Success');
    }
}

handleResponse(200); // Ок
// handleResponse(201); // Ошибка! Argument of type '201' is not assignable to parameter of type '200 | 404 | 500'

as const делает каждое свойство readonly и превращает значения в литеральные типы. Это мощный инструмент для создания типобезопасных конфигураций.

Способ №5: Приватные константы с # и геттеры

В современном TypeScript/JavaScript можно использовать приватные поля (#) вместе с геттерами для создания констант, которые нельзя изменить даже изнутри класса после инициализации.

class SecretConfig {
    #secretKey: string;

    constructor(key: string) {
        this.#secretKey = key;
    }

    get secretKey(): string {
        return this.#secretKey;
    }
}

const config = new SecretConfig('my-secret');
console.log(config.secretKey); // 'my-secret'
// config.#secretKey = 'new'; // Ошибка! Property '#secretKey' is not accessible outside class

Этот подход полезен для конфиденциальных данных, которые должны быть доступны только для чтения извне.

Продвинутый паттерн: Константы с дженериками

Если вам нужно создавать классы с константами, которые зависят от типа, используйте дженерики.

class ConfigManager<T extends Record<string, string | number>> {
    readonly config: T;

    constructor(config: T) {
        this.config = Object.freeze(config);
    }

    get<K extends keyof T>(key: K): T[K] {
        return this.config[key];
    }
}

const dbConfig = new ConfigManager({
    host: 'localhost',
    port: 5432,
    database: 'mydb'
});

console.log(dbConfig.get('host')); // 'localhost'
// dbConfig.get('invalid'); // Ошибка! Argument of type '"invalid"' is not assignable to parameter of type '"host" | "port" | "database"'

Теперь вы можете создавать типобезопасные конфигурации для любых сценариев.

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

  • Используйте UPPER_CASE для констант — это негласное правило, которое делает код читаемым: static readonly MAX_VALUE = 100.
  • Не злоупотребляйте enum для простых случаев — если у вас всего две

Блоги

Рекомендаций по книгам