Как использовал пространства имен TypeScript?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Пространства имен (Namespaces) в TypeScript
Пространства имён — это способ группировать код и избежать конфликтов имён. Однако это устаревший подход, и в современном TypeScript предпочитают модули. Я расскажу оба варианта.
Что такое namespace
Namespace объединяет переменные, функции, классы и интерфейсы в одно имя:
namespace Utils {
export function sum(a: number, b: number): number {
return a + b;
}
export const PI = 3.14;
}
// Использование
Utils.sum(5, 3); // 8
console.log(Utils.PI); // 3.14
Всё что не экспортировано — приватно:
namespace Utils {
// Приватная переменная
const internalSecret = 'secret';
export function getSecret() {
return internalSecret; // Доступна здесь
}
}
Utils.internalSecret; // ❌ Error: не видна снаружи
Utils.getSecret(); // ✅ OK
Вложенные namespaces
namespace Application {
export namespace UI {
export class Button {
click() { /* ... */ }
}
}
export namespace Services {
export class APIService {
fetch() { /* ... */ }
}
}
}
// Использование
const btn = new Application.UI.Button();
const api = new Application.Services.APIService();
Пример: Организация геометрических функций
namespace Geometry {
export interface Point {
x: number;
y: number;
}
export class Circle {
constructor(public center: Point, public radius: number) {}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
export class Rectangle {
constructor(public width: number, public height: number) {}
getArea(): number {
return this.width * this.height;
}
}
}
const center: Geometry.Point = { x: 0, y: 0 };
const circle = new Geometry.Circle(center, 5);
console.log(circle.getArea()); // 78.53...
Ambient namespaces (глобальные)
Если нужно расширить глобальный объект (не рекомендуется):
namespace NodeJS {
interface Global {
myCustomValue: string;
}
}
global.myCustomValue = 'hello'; // Теперь типизировано
❌ Почему namespaces — плохая идея
1. Конфликты с модулями
// namespace.ts
namespace Utils {
export function sum(a: number, b: number) { return a + b; }
}
// Как импортировать?
// Нельзя просто import { Utils }...
// Нужен <reference path="namespace.ts" /> (очень старый стиль)
2. Сложность с управлением
// Одна папка не = одно пространство
utils/math.ts:
namespace Utils { export function sum... }
utils/string.ts:
namespace Utils { export function trim... }
// Оба Utils "сталкиваются" но не явно видно где определено sum или trim
3. Несовместимость с модульной системой
// Это не работает правильно с import/export
namespace Utils { export function sum... }
export { Utils }; // Странно выглядит
✅ Современный подход: ES Modules
Вместо namespace используй модули:
// math.ts
export function sum(a: number, b: number): number {
return a + b;
}
export const PI = 3.14;
// string.ts
export function trim(str: string): string {
return str.trim();
}
// index.ts (namespace replacement)
export * as Math from './math';
export * as String from './string';
// main.ts
import * as Utils from './index';
Utils.Math.sum(5, 3); // 8
Utils.String.trim(' hi '); // 'hi'
Или явные импорты:
import { sum, PI } from './math';
import { trim } from './string';
sum(5, 3);
trim(' hi ');
Когда namespace все ещё используется
1. Legacy код (старые проекты)
Если проект использует namespace — понимай как они работают:
namespace LegacyApp {
export class Service {
static instance: Service;
}
}
namespace LegacyApp {
export class Service {
// Расширяем существующий namespace
}
}
2. Глобальные типы (редко)
// Расширение встроенных типов
declare global {
interface Array<T> {
custom(): void;
}
}
Array.prototype.custom = function() { /* ... */ };
Практический пример: Правильный способ структурировать код
// Вместо namespace — модули
// src/features/auth/types.ts
export interface User {
id: string;
email: string;
}
export interface AuthResponse {
token: string;
user: User;
}
// src/features/auth/service.ts
import type { User, AuthResponse } from './types';
export class AuthService {
async login(email: string, password: string): Promise<AuthResponse> {
// Implementation
return { token: '...', user: { id: '1', email } };
}
}
// src/features/auth/index.ts (barrel export)
export * from './types';
export { AuthService } from './service';
// src/main.ts
import { AuthService, type User, type AuthResponse } from './features/auth';
const auth = new AuthService();
const response = await auth.login('user@example.com', 'password');
Сравнение
| Aspect | Namespace | Modules |
|---|---|---|
| Импорт | <reference> или глобально | import/export |
| Область видимости | Глобальная область + namespace | File scope |
| Tree-shaking | Нет | Да |
| IDE support | Слабый | Отличный |
| Модульность | Слабая | Сильная |
| Рекомендуется | Нет | Да |
Когда я использовал namespace (реальный пример)
В легаси проекте с jQuery было:
namespace MyApp.Pages {
export class Dashboard {
render() { /* ... */ }
}
}
namespace MyApp.Components {
export class Modal {
open() { /* ... */ }
}
}
// В HTML
<script src="namespace.js"></script>
<script src="components/modal.js"></script>
<script src="pages/dashboard.js"></script>
<script>
const dashboard = new MyApp.Pages.Dashboard();
</script>
Но потом это переписали на модули + React.
Заключение
Для нового кода: используй ES modules с организацией по папкам
src/
features/
auth/
types.ts
service.ts
index.ts
dashboard/
types.ts
components.ts
index.ts
Для старого кода: понимай namespaces но не создавай новые.
Namespaces — это TypeScript специфическая фича которая не используется в современном JavaScript экосистеме. Модули — стандарт ES6+ и вот что нужно использовать в 2024+.