Avatar ·

Как правильно объявить константы в классе TypeScript?

Всем привет! Я только начал изучать TypeScript и столкнулся с проблемой. В обычном JavaScript я просто использовал `const` для констант, но когда пытаюсь сделать то же самое внутри класса, TypeScript ругается. Мне нужно, чтобы у моего класса `MathUtils` были константы, например, число Пи, ускорение свободного падения и скорость света. Я хочу, чтобы эти значения были общими для всех экземпляров класса и их нельзя было изменить. Я пробовал несколько вариантов, но ни один не работает так, как я ожидаю: ```typescript class MathUtils { // Вариант 1: просто const const PI = 3.14159; // Ошибка: 'const' declarations are not allowed in classes // Вариант 2: readonly readonly G = 9.81; // Работает, но нужно создавать экземпляр // Вариант 3: static readonly static readonly C = 299792458; // Вроде работает, но как правильно? } ``` Проблема в том, что с `readonly` мне нужно создавать экземпляр класса, чтобы получить доступ к константе: `new MathUtils().G`. А с `static readonly` я могу обращаться как `MathUtils.C`, но я не уверен, что это правильный подход. Подскажите, как в TypeScript принято объявлять константы внутри класса? Есть ли какая-то лучшая практика? И можно ли сделать так, чтобы константы были доступны и через экземпляр, и через сам класс? Заранее спасибо за помощь! <<>>

Правильный способ: комбинация static и readonly

В TypeScript для объявления констант внутри класса используется связка модификаторов static readonly. Это самый правильный и распространённый подход. Давайте разберём, почему это работает именно так.

Почему const не работает внутри класса?

TypeScript (как и JavaScript) запрещает использование const внутри классов. const предназначен для объявления констант на уровне переменных, а не свойств класса. Для свойств классов используются другие механизмы.

Решение: static readonly

Вот как правильно объявить константы в вашем классе:

class MathUtils {
  // Статическая константа — доступна через класс
  static readonly PI: number = 3.141592653589793;
  static readonly G: number = 9.80665; // Ускорение свободного падения
  static readonly C: number = 299792458; // Скорость света в м/с

  // Обычное readonly свойство — доступно через экземпляр
  readonly name: string = "Calculator";

  // Можно комбинировать
  static readonly E: number = Math.E;

  // Статический метод, использующий константы
  static calculateCircleArea(radius: number): number {
    return MathUtils.PI * radius * radius;
  }
}

// Использование:
console.log(MathUtils.PI);       // 3.141592653589793
console.log(MathUtils.G);        // 9.80665
console.log(MathUtils.C);        // 299792458
console.log(MathUtils.calculateCircleArea(5)); // 78.53981633974483

// Попытка изменить вызовет ошибку компиляции:
// MathUtils.PI = 3; // Error: Cannot assign to 'PI' because it is a read-only property

Почему это работает?

  • static — делает свойство принадлежащим самому классу, а не его экземплярам. Вы обращаетесь к нему через MathUtils.PI, а не через new MathUtils().PI.
  • readonly — запрещает изменение значения после инициализации. Это аналог const для свойств класса.

Если нужен доступ и через экземпляр

Если вам критично, чтобы константа была доступна и через экземпляр класса, можно добавить геттер:

class MathUtils {
  static readonly PI: number = 3.14159;

  // Геттер для доступа через экземпляр
  get PI(): number {
    return MathUtils.PI;
  }
}

const utils = new MathUtils();
console.log(utils.PI);        // 3.14159 — через экземпляр
console.log(MathUtils.PI);    // 3.14159 — через класс

Лучшие практики

  • Используйте static readonly для настоящих констант, общих для всех экземпляров.
  • Именуйте константы в UPPER_CASE (общепринятое соглашение).
  • Для простых констант, не связанных с классом, лучше использовать const на уровне модуля.
  • Если константа зависит от конфигурации, рассмотрите использование static readonly с инициализацией в блоке static.

Надеюсь, теперь стало понятнее! static readonly — это именно то, что вам нужно.

<<>>

Альтернативный подход: Константы на уровне модуля

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

// constants.ts
export const PI = 3.141592653589793;
export const G = 9.80665;
export const C = 299792458;

// math-utils.ts
import { PI, G, C } from './constants';

export class MathUtils {
  static calculateCircleArea(radius: number): number {
    return PI * radius * radius;
  }
}

Плюсы этого подхода:

  • Разделение ответственности — константы отдельно, логика отдельно.
  • Переиспользование — константы легко импортировать в любой файл.
  • Простота — не нужно заморачиваться с static readonly.
  • Tree-shaking — если константа не используется, она не попадёт в финальную сборку.

Какой подход выбрать? Если константы тесно связаны с классом и используются только в его методах — смело используйте static readonly. Если же они нужны в разных местах — выносите их в отдельный модуль. Оба варианта правильные, выбирайте по ситуации.