Modules and Namespaces in TypeScript
A Guide to TypeScript's Modules and Namespaces
Welcome back to our TypeScript series!
Today, we’ll explore modules and namespaces, two features that help you organize your code. While both provide ways to structure your code, they serve different purposes and have different use cases. We will cover the basics, usage, and best practices for both modules and namespaces.
1. Modules
Modules are a way to organize code into reusable blocks. They provide a way to split your code into separate files and export and import functionality between them. Modules can be thought of as files or external modules.
1.1. Exporting and Importing
Exporting from a Module
To make code available outside a module, you use the export
keyword.
// file: mathUtils.ts
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14;
Importing from a Module
To use exported code from another module, you use the import
keyword.
// file: main.ts
import { add, PI } from './mathUtils';
console.log(add(5, 3)); // Output: 8
console.log(PI); // Output: 3.14
1.2. Default Exports
A module can have a default export, which simplifies importing.
Default Export
// file: mathUtils.ts
export default function subtract(a: number, b: number): number {
return a - b;
}
Default Import
// file: main.ts
import subtract from './mathUtils';
console.log(subtract(10, 4)); // Output: 6
1.3. Re-exporting
Modules can re-export entities from other modules, allowing for easier aggregation.
1.3.1 How to Re-exporting
You can re-export items using the export
keyword along with from
to specify the module you're re-exporting from.
// file: basicMath.ts
export * from './mathUtils';
1.3.2 Example: Re-exporting
Consider three modules, each exporting some functionality:
// file: mathUtils.ts
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
// file: constants.ts
export const PI = 3.14;
export const E = 2.71;
// file: strings.ts
export function greet(name: string): string {
return `Hello, ${name}!`;
}
You can create a central module that re-exports items from these modules:
// file: index.ts
export { add, subtract } from './mathUtils';
export { PI, E } from './constants';
export { greet } from './strings';
Now, you can import everything from the index.ts
module:
// file: main.ts
import { add, subtract, PI, E, greet } from './index';
console.log(add(5, 3)); // Output: 8
console.log(subtract(10, 4)); // Output: 6
console.log(PI); // Output: 3.14
console.log(E); // Output: 2.71
console.log(greet("Alice")); // Output: Hello, Alice!
1.3.3 Re-exporting with ` export *`
You can also re-export everything from a module using export *
.
// file: index.ts
export * from './mathUtils';
export * from './constants';
export * from './strings';
This re-exports all named exports from mathUtils
, constants
, and strings
.
2. Namespaces
Namespaces are a way to organize code within a single file or across multiple files in the global scope. They are useful for organizing large applications and avoiding name collisions.
2.1. Defining a Namespace
You define a namespace using the namespace
keyword.
namespace MathUtils {
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14;
}
Using a Namespace
To use the functions or variables defined in a namespace, you need to reference them with the namespace name.
console.log(MathUtils.add(5, 3)); // Output: 8
console.log(MathUtils.PI); // Output: 3.14
2.2. Nested Namespaces
Namespaces can be nested to create a hierarchical structure.
namespace Geometry {
export namespace Circle {
export function circumference(radius: number): number {
return 2 * MathUtils.PI * radius;
}
}
}
console.log(Geometry.Circle.circumference(10)); // Output: 62.8
3. Modules vs. Namespaces
While both modules and namespaces can be used to organize code, they have different use cases:
Modules: Use modules to organize code into reusable files and manage dependencies. Modules are the preferred way to organize code in modern TypeScript and JavaScript projects, especially for large-scale applications.
Namespaces: Use namespaces to organize code within a file or across multiple files in the global scope. They are useful for organizing libraries or frameworks where global scope management is necessary.
4. Best Practices
Prefer Modules: Use modules over namespaces for better maintainability and scalability.
Consistent Imports and Exports: Use named exports for consistency and avoid default exports unless necessary.
Organize Code: Break down large modules into smaller, more manageable files and use re-exports to aggregate them.
5. Example Code
Here’s an example that combines various concepts of modules and namespaces:
// file: mathUtils.ts
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14;
// file: geometry.ts
import { PI } from './mathUtils';
export namespace Geometry {
export namespace Circle {
export function circumference(radius: number): number {
return 2 * PI * radius;
}
}
export namespace Rectangle {
export function area(length: number, width: number): number {
return length * width;
}
}
}
// file: main.ts
import { add } from './mathUtils';
import { Geometry } from './geometry';
console.log(add(5, 3)); // Output: 8
console.log(Geometry.Circle.circumference(10)); // Output: 62.8
console.log(Geometry.Rectangle.area(5, 10)); // Output: 50
Summary
Today, we explored modules and namespaces in TypeScript, covering the basics, usage, and best practices for both. Modules are the preferred way to organize code in modern TypeScript, while namespaces are useful for managing code in the global scope. Understanding these concepts will help you structure your code effectively and maintainably.
Next time, we’ll dive into decorators in TypeScript. Stay tuned!