TypeScript is a superset of JavaScript that adds static types and other powerful features to the language. One of the key aspects of TypeScript is its ability to organize and modularize code through the use of modules and namespaces. This chapter will explore these concepts in detail, from the basics to advanced usage, providing comprehensive explanations and examples along the way.
Modules are a way to encapsulate and organize code into separate files and reuse them across the application. They help in managing dependencies, avoiding global scope pollution, and making the code more maintainable.
In TypeScript, each file is considered a module. Modules can export and import functionality from other modules using export and import keywords.
You can export variables, functions, classes, or interfaces from a module.
// mathUtils.ts
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
You can import the exported functionality into another module.
// main.ts
import { add, subtract } from './mathUtils';
console.log(add(5, 3)); // Output: 8
console.log(subtract(5, 3)); // Output: 2
mathUtils.ts exports two functions, add and subtract.main.ts imports these functions and uses them.A module can have a single default export, which can be a variable, function, class, or object.
// logger.ts
export default function log(message: string): void {
console.log(message);
}
// app.ts
import log from './logger';
log('Hello, TypeScript!'); // Output: Hello, TypeScript!
Explanation:
logger.ts exports a default function log.app.ts imports this default function and uses it to log a message.You can mix named exports and default exports in a module.
// utilities.ts
export function greet(name: string): string {
return `Hello, ${name}!`;
}
export default function farewell(name: string): string {
return `Goodbye, ${name}!`;
}
// index.ts
import farewell, { greet } from './utilities';
console.log(greet('Alice')); // Output: Hello, Alice!
console.log(farewell('Alice')); // Output: Goodbye, Alice!
utilities.ts exports a named function greet and a default function farewell.index.ts imports both the named and default exports and uses them.You can re-export functionality from one module in another module.
// mathOperations.ts
export { add, subtract } from './mathUtils';
// app.ts
import { add, subtract } from './mathOperations';
console.log(add(10, 5)); // Output: 15
console.log(subtract(10, 5)); // Output: 5
mathOperations.ts re-exports the functions add and subtract from mathUtils.ts.app.ts imports these re-exported functions and uses them.You can import all exports from a module as a single object.
// constants.ts
export const PI = 3.14;
export const E = 2.71;
// main.ts
import * as constants from './constants';
console.log(constants.PI); // Output: 3.14
console.log(constants.E); // Output: 2.71
constants.ts exports two constants, PI and E.main.ts imports everything from constants.ts as a single object constants and accesses the constants.You can dynamically import modules at runtime using import().
// utils.ts
export function sayHello() {
console.log('Hello!');
}
// main.ts
async function loadUtils() {
const utils = await import('./utils');
utils.sayHello(); // Output: Hello!
}
loadUtils();
utils.ts exports a function sayHello.main.ts dynamically imports utils.ts using import(), and then calls the sayHello function.Namespaces in TypeScript are a way to organize and group related code. They provide a way to avoid global scope pollution by encapsulating code within a namespace. Unlike modules, namespaces can be nested and declared multiple times.
You can declare a namespace using the namespace keyword.
namespace MathUtils {
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
}
console.log(MathUtils.add(5, 3)); // Output: 8
console.log(MathUtils.subtract(5, 3)); // Output: 2
MathUtils namespace contains two functions, add and subtract.MathUtils.You can nest namespaces to further organize code.
namespace Utilities {
export namespace Math {
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
}
export namespace String {
export function toUpperCase(str: string): string {
return str.toUpperCase();
}
export function toLowerCase(str: string): string {
return str.toLowerCase();
}
}
}
console.log(Utilities.Math.add(10, 5)); // Output: 15
console.log(Utilities.String.toUpperCase('hello')); // Output: HELLO
Utilities namespace contains two nested namespaces: Math and String.Math namespace contains two functions, add and subtract.String namespace contains two functions, toUpperCase and toLowerCase.You can declare the same namespace multiple times, and TypeScript will merge them.
namespace App {
export function initialize() {
console.log('App initialized');
}
}
namespace App {
export function shutdown() {
console.log('App shutdown');
}
}
App.initialize(); // Output: App initialized
App.shutdown(); // Output: App shutdown
Explanation:
App namespace is declared twice, with each declaration adding a new function.App namespace.You can combine modules and namespaces to leverage the benefits of both.
You can declare a namespace inside a module.
// shapes.ts
export namespace Shapes {
export class Circle {
constructor(public radius: number) {}
area(): number {
return Math.PI * this.radius * this.radius;
}
}
export class Square {
constructor(public size: number) {}
area(): number {
return this.size * this.size;
}
}
}
// main.ts
import { Shapes } from './shapes';
const circle = new Shapes.Circle(5);
console.log(circle.area()); // Output: 78.53981633974483
const square = new Shapes.Square(4);
console.log(square.area()); // Output: 16
shapes.ts declares a Shapes namespace inside a module.Shapes namespace contains two classes, Circle and Square.main.ts imports the Shapes namespace and uses the classes to create instances and calculate areas.You can augment global namespaces by merging them with module declarations.
// globals.d.ts
declare namespace NodeJS {
interface Global {
appName: string;
}
}
// app.ts
global.appName = 'MyApp';
console.log(global.appName); // Output: MyApp
globals.d.ts declares an augmentation of the NodeJS.Global interface to include a new property appName.app.ts sets the global.appName property and logs it to the console.Modules and namespaces in TypeScript provide powerful tools for organizing and modularizing code. Modules offer a way to encapsulate and reuse code across files, while namespaces allow for grouping related code and avoiding global scope pollution. By understanding and utilizing these features, you can write more maintainable, scalable, and type-safe TypeScript code. Happy coding! ❤️
