При работе с крупными проектами возникает необходимость структурировать код, предотвращать конфликты имён и создавать удобные наборы констант. В экосистеме TypeScript для этих задач используются namespace и enum. Поскольку SWC выполняет трансформацию TypeScript-кода в JavaScript, поддержка этих конструкций играет важную роль при миграции проектов и ускорении сборки.
Обработка namespace и enum относится к числу возможностей SWC, связанных с компиляцией TypeScript. Во время трансформации компилятор удаляет типовую информацию и преобразует конструкции, отсутствующие в стандартном JavaScript, в эквивалентный исполняемый код.
Namespace представляет собой механизм логической группировки сущностей внутри одного пространства имён.
Пример объявления:
namespace Geometry {
export const PI = 3.14159;
export function area(radius: number): number {
return PI * radius * radius;
}
}
Использование:
const result = Geometry.area(10);
Namespace появился ещё до широкого распространения ES-модулей и использовался для организации кода внутри глобальной области видимости.
Поскольку JavaScript не содержит встроенной конструкции namespace, SWC преобразует её в объект и самовызывающуюся функцию.
Исходный код:
namespace Utils {
export const version = "1.0";
export function print() {
console.log(version);
}
}
После компиляции получается код, близкий к следующему:
var Utils;
(function(Utils) {
Utils.version = "1.0";
function print() {
console.log(Utils.version);
}
Utils.print = print;
})(Utils || (Utils = {}));
В процессе трансформации:
Члены пространства имён становятся доступными только после использования
ключевого слова export.
Пример:
namespace UserSystem {
const secret = "token";
export const name = "Admin";
export function getName() {
return name;
}
}
После трансформации:
var UserSystem;
(function(UserSystem) {
const secret = "token";
UserSystem.name = "Admin";
function getName() {
return UserSystem.name;
}
UserSystem.getName = getName;
})(UserSystem || (UserSystem = {}));
Неэкспортируемая переменная secret остаётся внутренней и
недоступна извне.
TypeScript позволяет создавать вложенные пространства имён.
Исходный код:
namespace Application {
export namespace Services {
export function load() {
console.log("Loading...");
}
}
}
Использование:
Application.Services.load();
SWC разворачивает такую структуру в несколько уровней объектов:
var Application;
(function(Application) {
var Services;
(function(Services) {
function load() {
console.log("Loading...");
}
Services.load = load;
})(Services = Application.Services || (Application.Services = {}));
})(Application || (Application = {}));
Каждый вложенный namespace становится вложенным объектом JavaScript.
Одной из особенностей TypeScript является возможность объединения нескольких объявлений namespace.
Исходный код:
namespace Config {
export const host = "localhost";
}
namespace Config {
export const port = 3000;
}
Использование:
console.log(Config.host);
console.log(Config.port);
SWC сохраняет механизм слияния через повторное использование одного объекта:
var Config;
(function(Config) {
Config.host = "localhost";
})(Config || (Config = {}));
(function(Config) {
Config.port = 3000;
})(Config || (Config = {}));
В результате объект содержит оба свойства.
Внутри пространства имён могут находиться классы.
Пример:
namespace Models {
export class User {
constructor(public name: string) {}
}
}
После компиляции:
var Models;
(function(Models) {
class User {
constructor(name) {
this.name = name;
}
}
Models.User = User;
})(Models || (Models = {}));
Класс становится свойством объекта namespace.
Интерфейсы существуют только на этапе проверки типов.
Исходный код:
namespace API {
export interface Response {
status: number;
}
export function send(): Response {
return { status: 200 };
}
}
После обработки SWC:
var API;
(function(API) {
function send() {
return {
status: 200
};
}
API.send = send;
})(API || (API = {}));
Интерфейс полностью удаляется из итогового JavaScript-кода.
Хотя SWC полностью поддерживает пространства имён TypeScript, в современных приложениях предпочтение обычно отдаётся ES-модулям.
Вместо:
namespace MathUtils {
export function sum(a: number, b: number) {
return a + b;
}
}
Чаще используется:
export function sum(a: number, b: number) {
return a + b;
}
SWC эффективно работает с обоими подходами, однако модульная система лучше интегрируется с современными инструментами сборки.
Enum представляет собой набор именованных констант.
Пример:
enum Status {
Pending,
Approved,
Rejected
}
Использование:
const current = Status.Approved;
JavaScript не содержит встроенной реализации enum, поэтому SWC создаёт соответствующую структуру объектов.
Для числовых перечислений SWC генерирует двустороннее отображение.
Исходный код:
enum Status {
Pending,
Approved,
Rejected
}
Приблизительный результат:
var Status;
(function(Status) {
Status[Status["Pending"] = 0] = "Pending";
Status[Status["Approved"] = 1] = "Approved";
Status[Status["Rejected"] = 2] = "Rejected";
})(Status || (Status = {}));
Появляются две формы доступа:
Status.Pending;
Результат:
0
И обратное отображение:
Status[0];
Результат:
"Pending"
После компиляции объект имеет структуру:
{
0: "Pending",
1: "Approved",
2: "Rejected",
Pending: 0,
Approved: 1,
Rejected: 2
}
Это позволяет выполнять преобразование в обе стороны:
console.log(Status.Pending);
console.log(Status[0]);
Вывод:
0
Pending
Значения могут задаваться вручную.
Исходный код:
enum HttpCode {
Ok = 200,
NotFound = 404,
InternalError = 500
}
Трансформация:
var HttpCode;
(function(HttpCode) {
HttpCode[HttpCode["Ok"] = 200] = "Ok";
HttpCode[HttpCode["NotFound"] = 404] = "NotFound";
HttpCode[HttpCode["InternalError"] = 500] = "InternalError";
})(HttpCode || (HttpCode = {}));
Доступ:
HttpCode.Ok;
Результат:
200
Строковые перечисления работают иначе.
Исходный код:
enum Role {
Admin = "ADMIN",
User = "USER",
Guest = "GUEST"
}
После обработки:
var Role;
(function(Role) {
Role["Admin"] = "ADMIN";
Role["User"] = "USER";
Role["Guest"] = "GUEST";
})(Role || (Role = {}));
Обратное отображение не создаётся.
Структура объекта:
{
Admin: "ADMIN",
User: "USER",
Guest: "GUEST"
}
Попытка выполнить:
Role["ADMIN"];
вернёт:
undefined
TypeScript допускает смешивание строковых и числовых значений.
Пример:
enum Mixed {
No = 0,
Yes = "YES"
}
После трансформации:
var Mixed;
(function(Mixed) {
Mixed[Mixed["No"] = 0] = "No";
Mixed["Yes"] = "YES";
})(Mixed || (Mixed = {}));
Такой подход встречается редко и обычно не рекомендуется из-за ухудшения читаемости кода.
Перечисления могут использовать вычисления.
Пример:
enum FileAccess {
None = 0,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write
}
SWC сохраняет вычисленные значения в результирующем коде:
var FileAccess;
(function(FileAccess) {
FileAccess[FileAccess["None"] = 0] = "None";
FileAccess[FileAccess["Read"] = 2] = "Read";
FileAccess[FileAccess["Write"] = 4] = "Write";
FileAccess[FileAccess["ReadWrite"] = 6] = "ReadWrite";
})(FileAccess || (FileAccess = {}));
После компиляции используются уже вычисленные константы.
Для повышения производительности TypeScript предоставляет конструкцию
const enum.
Исходный код:
const enum Direction {
Up,
Down,
Left,
Right
}
Использование:
const value = Direction.Left;
При инлайнинге значение может быть подставлено напрямую:
const value = 2;
В таком случае объект перечисления вообще не создаётся.
Обычный enum:
Status.Pending
После компиляции:
Status.Pending
Требует обращения к объекту.
const enum:
Direction.Left
После подстановки:
2
Преимущества:
Обе конструкции могут использоваться совместно.
Исходный код:
namespace System {
export enum State {
Start,
Stop
}
}
После трансформации:
var System;
(function(System) {
let State;
(function(State) {
State[State["Start"] = 0] = "Start";
State[State["Stop"] = 1] = "Stop";
})(State = System.State || (System.State = {}));
})(System || (System = {}));
Использование остаётся прежним:
System.State.Start;
При обработке namespace и enum SWC одновременно удаляет все элементы системы типов:
namespace Users {
export interface User {
id: number;
}
export type UserId = number;
export enum Role {
Admin,
User
}
}
После компиляции остаётся только исполняемый код перечисления:
var Users;
(function(Users) {
let Role;
(function(Role) {
Role[Role["Admin"] = 0] = "Admin";
Role[Role["User"] = 1] = "User";
})(Role = Users.Role || (Users.Role = {}));
})(Users || (Users = {}));
Интерфейсы и псевдонимы типов полностью исчезают.
Namespace целесообразен, когда требуется поддержка старого TypeScript-кода или совместимость с существующими архитектурными решениями.
ES-модули предпочтительнее, поскольку они лучше оптимизируются современными сборщиками и упрощают анализ зависимостей.
Числовые enum подходят в случаях, когда требуется обратное отображение значения в имя.
Строковые enum удобнее для сериализации данных, взаимодействия с API и хранения значений в базах данных.
Const enum позволяет минимизировать объём итогового кода и используется в производительных участках приложения.
SWC корректно преобразует все основные разновидности namespace и enum, сохраняя их семантику и обеспечивая высокую скорость компиляции TypeScript-проектов.