Organize and structure your TypeScript code with modules and namespaces.
ES6 Modules
Export
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
export const PI = 3.14159;
// Default export
export default function multiply(a: number, b: number): number {
return a * b;
}
Import
// app.ts
import multiply, { add, subtract, PI } from "./math";
console.log(add(5, 3));
console.log(multiply(2, 4));
console.log(PI);
Re-export
// utils.ts
export { add, subtract } from "./math";
export { formatDate } from "./date";
Namespace Imports
import * as Math from "./math";
console.log(Math.add(5, 3));
console.log(Math.PI);
Type-Only Imports/Exports
Import or export only types (TypeScript 3.8+).
// types.ts
export type User = {
name: string;
age: number;
};
export interface Product {
id: number;
name: string;
}
// app.ts
import type { User, Product } from "./types";
// or
import { type User, type Product } from "./types";
Namespaces
Organize code into logical groups (legacy, prefer modules).
namespace MathUtils {
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
export const PI = 3.14159;
}
// Usage
MathUtils.add(5, 3);
console.log(MathUtils.PI);
Nested Namespaces
namespace Geometry {
export namespace Circle {
export function area(radius: number): number {
return Math.PI * radius ** 2;
}
}
export namespace Rectangle {
export function area(width: number, height: number): number {
return width * height;
}
}
}
Geometry.Circle.area(5);
Geometry.Rectangle.area(4, 6);
Merging Namespaces
namespace Geometry {
export function calculate() {
// ...
}
}
namespace Geometry {
export function draw() {
// ...
}
}
// Both functions are available
Geometry.calculate();
Geometry.draw();
Module Resolution
TypeScript resolves modules using different strategies.
Relative Imports
import { something } from "./local-file";
import { something } from "../parent-directory/file";
Non-Relative Imports
// With path mapping in tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"utils/*": ["src/utils/*"]
}
}
}
// Usage
import { something } from "@/components/Button";
import { helper } from "utils/helpers";
Declaration Merging
Interfaces and namespaces can be merged.
Interface Merging
interface User {
name: string;
}
interface User {
age: number;
}
// Merged: { name: string; age: number; }
Namespace Merging
namespace MyLib {
export function doSomething() {}
}
namespace MyLib {
export function doSomethingElse() {}
}
Ambient Modules
Declare modules for external libraries.
// declarations.d.ts
declare module "my-library" {
export function hello(): string;
export const version: string;
}
// Usage
import { hello, version } from "my-library";
Exercises
Exercise 1: ES6 Modules
// Create a module 'calculator.ts' with:
// - add, subtract, multiply, divide functions
// - Export them individually
// - Create a default export for a Calculator class
// Import and use them in another file
Exercise 2: Type-Only Imports
// Create a types file with interfaces
// Import only the types in another file
// Verify that types are not included in the compiled output
Exercise 3: Namespaces
// Create a namespace 'StringUtils' with:
// - capitalize function
// - reverse function
// - truncate function
// Use them in your code
Exercise 4: Path Mapping
// Set up path mapping in tsconfig.json
// Create imports using the mapped paths
// Verify compilation works
Exercise 5: Declaration Merging
// Create an interface in one file
// Extend it in another file using declaration merging
// Use the merged interface
Best Practices
- Prefer ES6 modules over namespaces - Modern standard
- Use type-only imports - When you only need types
- Organize with folders - Group related modules
- Use path mapping - For cleaner imports
- Avoid namespace merging - Can be confusing
Common Mistakes
- Mixing module systems
// ❌ Bad - mixing CommonJS and ES6
const fs = require("fs");
import { something } from "./file";
- Circular dependencies
// fileA.ts
import { b } from "./fileB";
// fileB.ts
import { a } from "./fileA"; // Circular!
- Not using type-only imports
// ❌ Types included in runtime code
import { User } from "./types";
// ✅ Types removed from runtime
import type { User } from "./types";
Next Steps
Learn about Decorators.