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

  1. Prefer ES6 modules over namespaces - Modern standard
  2. Use type-only imports - When you only need types
  3. Organize with folders - Group related modules
  4. Use path mapping - For cleaner imports
  5. Avoid namespace merging - Can be confusing

Common Mistakes

  1. Mixing module systems
// ❌ Bad - mixing CommonJS and ES6
const fs = require("fs");
import { something } from "./file";
  1. Circular dependencies
// fileA.ts
import { b } from "./fileB";

// fileB.ts
import { a } from "./fileA"; // Circular!
  1. 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.