TypeScript provides namespaces and modules for code organization. Here's when and how to use each.
ES Modules (Recommended)#
1// math.ts - Export functions
2export function add(a: number, b: number): number {
3 return a + b;
4}
5
6export function subtract(a: number, b: number): number {
7 return a - b;
8}
9
10export const PI = 3.14159;
11
12// Default export
13export default class Calculator {
14 calculate(expression: string): number {
15 // Implementation
16 return 0;
17 }
18}
19
20// main.ts - Import and use
21import Calculator, { add, subtract, PI } from './math';
22import * as math from './math';
23
24console.log(add(2, 3)); // 5
25console.log(math.subtract(5, 2)); // 3
26
27const calc = new Calculator();Re-exporting#
1// shapes/circle.ts
2export interface Circle {
3 radius: number;
4}
5
6export function area(circle: Circle): number {
7 return Math.PI * circle.radius ** 2;
8}
9
10// shapes/rectangle.ts
11export interface Rectangle {
12 width: number;
13 height: number;
14}
15
16export function area(rect: Rectangle): number {
17 return rect.width * rect.height;
18}
19
20// shapes/index.ts - Barrel file
21export * from './circle';
22export * as rectangle from './rectangle';
23export { area as circleArea } from './circle';
24export { area as rectangleArea } from './rectangle';
25
26// main.ts
27import { Circle, circleArea, rectangle } from './shapes';
28
29const circle: Circle = { radius: 5 };
30console.log(circleArea(circle));
31console.log(rectangle.area({ width: 4, height: 3 }));Namespaces (Legacy Pattern)#
1// Namespaces group related code
2namespace Validation {
3 export interface StringValidator {
4 isValid(s: string): boolean;
5 }
6
7 export class EmailValidator implements StringValidator {
8 isValid(s: string): boolean {
9 return s.includes('@');
10 }
11 }
12
13 export class ZipCodeValidator implements StringValidator {
14 isValid(s: string): boolean {
15 return /^\d{5}$/.test(s);
16 }
17 }
18
19 // Internal helper (not exported)
20 function sanitize(s: string): string {
21 return s.trim().toLowerCase();
22 }
23}
24
25// Usage
26const emailValidator = new Validation.EmailValidator();
27console.log(emailValidator.isValid('test@example.com'));Nested Namespaces#
1namespace App {
2 export namespace Models {
3 export interface User {
4 id: number;
5 name: string;
6 }
7
8 export interface Post {
9 id: number;
10 title: string;
11 authorId: number;
12 }
13 }
14
15 export namespace Services {
16 export class UserService {
17 getUser(id: number): Models.User {
18 return { id, name: 'John' };
19 }
20 }
21
22 export class PostService {
23 getPosts(userId: number): Models.Post[] {
24 return [];
25 }
26 }
27 }
28}
29
30// Usage
31const userService = new App.Services.UserService();
32const user: App.Models.User = userService.getUser(1);Declaration Merging#
1// Namespaces can merge with classes, functions, and enums
2
3// Class + Namespace
4class Album {
5 label: Album.AlbumLabel;
6}
7
8namespace Album {
9 export interface AlbumLabel {
10 name: string;
11 }
12}
13
14// Function + Namespace
15function buildLabel(name: string): string {
16 return buildLabel.prefix + name;
17}
18
19namespace buildLabel {
20 export let prefix = 'Hello, ';
21}
22
23console.log(buildLabel('World')); // 'Hello, World'
24
25// Enum + Namespace
26enum Color {
27 Red = 1,
28 Green = 2,
29 Blue = 4,
30}
31
32namespace Color {
33 export function mix(c1: Color, c2: Color): Color {
34 return c1 | c2;
35 }
36}
37
38console.log(Color.mix(Color.Red, Color.Blue)); // 5Ambient Namespaces#
1// global.d.ts - Declare external library
2declare namespace JQuery {
3 interface AjaxSettings {
4 url: string;
5 type?: string;
6 data?: any;
7 }
8
9 interface JQueryStatic {
10 ajax(settings: AjaxSettings): void;
11 (selector: string): JQueryElement;
12 }
13
14 interface JQueryElement {
15 html(): string;
16 html(content: string): JQueryElement;
17 click(handler: () => void): JQueryElement;
18 }
19}
20
21declare const $: JQuery.JQueryStatic;
22
23// Usage (no import needed)
24$.ajax({ url: '/api/data' });
25$('#button').click(() => console.log('Clicked'));Module Augmentation#
1// Extend existing module
2import { Observable } from 'rxjs';
3
4// Augment the module
5declare module 'rxjs' {
6 interface Observable<T> {
7 customMethod(): Observable<T>;
8 }
9}
10
11// Implement the augmentation
12Observable.prototype.customMethod = function () {
13 return this;
14};
15
16// Now available on all Observables
17import { of } from 'rxjs';
18of(1, 2, 3).customMethod();Global Augmentation#
1// Add to global scope
2declare global {
3 interface Window {
4 myApp: {
5 version: string;
6 init(): void;
7 };
8 }
9
10 interface Array<T> {
11 first(): T | undefined;
12 last(): T | undefined;
13 }
14}
15
16// Implement
17Array.prototype.first = function () {
18 return this[0];
19};
20
21Array.prototype.last = function () {
22 return this[this.length - 1];
23};
24
25// Usage
26window.myApp = {
27 version: '1.0.0',
28 init() {
29 console.log('App initialized');
30 },
31};
32
33[1, 2, 3].first(); // 1
34[1, 2, 3].last(); // 3
35
36export {}; // Make this a moduleWhen to Use What#
1// USE MODULES (ES Modules) when:
2// - Modern projects
3// - Node.js or bundled browser code
4// - Need tree-shaking
5// - Working with npm packages
6
7// math.ts
8export const add = (a: number, b: number) => a + b;
9
10// main.ts
11import { add } from './math';
12
13// USE NAMESPACES when:
14// - Organizing global declarations
15// - Augmenting existing types
16// - Legacy codebases
17// - Browser scripts without bundler
18
19namespace MyApp {
20 export function init() {}
21}Module Organization#
1// Recommended project structure
2
3// src/
4// models/
5// index.ts
6// user.ts
7// post.ts
8// services/
9// index.ts
10// user.service.ts
11// post.service.ts
12// utils/
13// index.ts
14// format.ts
15// validate.ts
16// index.ts
17
18// src/models/user.ts
19export interface User {
20 id: number;
21 name: string;
22 email: string;
23}
24
25// src/models/index.ts
26export * from './user';
27export * from './post';
28
29// src/services/user.service.ts
30import type { User } from '../models';
31
32export class UserService {
33 async getUser(id: number): Promise<User> {
34 // Implementation
35 }
36}
37
38// src/index.ts
39export * from './models';
40export * from './services';
41export * from './utils';
42
43// Consumer
44import { User, UserService } from './src';Best Practices#
Modules:
✓ Use ES modules for new code
✓ Use barrel files (index.ts)
✓ Import types with 'import type'
✓ Organize by feature/domain
Namespaces:
✓ Use for global declarations
✓ Use for type augmentation
✓ Keep flat when possible
✓ Document clearly
Avoid:
✗ Mixing modules and namespaces
✗ Deep namespace nesting
✗ Namespaces in modern apps
✗ Circular dependencies
Migration:
✓ Convert namespaces to modules
✓ Use barrel files for grouping
✓ Keep ambient declarations
✓ Update imports gradually
Conclusion#
ES Modules are the standard for modern TypeScript. Use them for code organization, tree-shaking, and npm package compatibility. Reserve namespaces for global declarations, type augmentation, and legacy codebases. When migrating, convert namespaces to modules using barrel files for grouping related exports.