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! ❤️