Module Pattern
The module pattern encapsulates related code into a self-contained unit with a public interface, hiding implementation details.
Classic IIFE module (pre-ES modules)
ts
const CartModule = (() => {
// private - not accessible outside
let items: CartItem[] = [];
let discount = 0;
function calculateTotal() {
return items.reduce((sum, i) => sum + i.price, 0) * (1 - discount);
}
// public interface
return {
addItem(item: CartItem) {
items = [...items, item];
},
removeItem(id: string) {
items = items.filter(i => i.id !== id);
},
setDiscount(d: number) { discount = d; },
getTotal() { return calculateTotal(); },
getItems() { return [...items]; }, // return copy - protect internal state
};
})();
CartModule.addItem({ id: '1', price: 29.99 });
CartModule.getTotal(); // 29.99
// CartModule.items -> undefined - privateES Modules - native module system
ts
// cart.ts
let items: CartItem[] = []; // module-private
export function addItem(item: CartItem) {
items = [...items, item];
}
export function getItems() {
return [...items];
}
export function getTotal() {
return items.reduce((sum, i) => sum + i.price, 0);
}
// private - not exported, not accessible
function applyTax(amount: number) {
return amount * 1.2;
}ts
// anywhere
import { addItem, getTotal } from './cart';Module patterns in practice
ts
// barrel file - re-export public API
// features/auth/index.ts
export { useAuth } from './useAuth';
export { AuthProvider } from './AuthProvider';
export type { User, AuthState } from './types';
// internal hooks, utils, api calls stay privateKey benefits
- Encapsulation - hide implementation, expose only what is needed.
- Namespace - no global variable pollution.
- Reusability - import only what you need.
- Tree shaking - bundlers can remove unused exports.
ES modules are the native module pattern. A barrel file (
index.ts) defines the public API of a feature folder, but use it with care: large barrels slow down dev servers and tests and can hurt tree shaking, because importing one export loads the whole folder. Many teams keep barrels only at feature boundaries or import files directly.
updated
did you know it?