Loading...
Loading...
Use when writing, fixing, or editing TypeScript modules, classes, file structure, declaration order, vertical formatting, dependency direction, cohesion, coupling, dependency construction, temporal coupling, public exports, wiring, or over-abstraction.
npx skill4agent add gosukiwi/clean-code-react clean-typescript-modules// Bad - formatter is irrelevant until much later
function sendDigest(users: User[], now: Date) {
const formatter = new Intl.DateTimeFormat("en-US");
const activeUsers = users.filter((user) => user.active);
const recipients = activeUsers.map((user) => user.email);
const subject = `Digest for ${recipients.length} users`;
return {
recipients,
subject,
generatedAt: formatter.format(now),
};
}
// Good - declaration sits with the idea that uses it
function sendDigest(users: User[], now: Date) {
const activeUsers = users.filter((user) => user.active);
const recipients = activeUsers.map((user) => user.email);
const subject = `Digest for ${recipients.length} users`;
const formatter = new Intl.DateTimeFormat("en-US");
return {
recipients,
subject,
generatedAt: formatter.format(now),
};
}export function priceOrder(order: Order): Money {
return applyDiscounts(calculateSubtotal(order), order.discounts);
}
function calculateSubtotal(order: Order): Money {
// ...
}
function applyDiscounts(subtotal: Money, discounts: Discount[]): Money {
// ...
}// Bad - one file changes for pricing, HTTP, and email policy
export async function fetchOrder() {}
export function calculateOrderTotal() {}
export function formatReceiptEmail() {}
// Good - each file has a focused reason to change
// order-client.ts, order-pricing.ts, receipt-email.ts// Bad - domain calculation knows the SDK shape
function calculateInvoiceTotal(invoice: Stripe.Invoice): number {
return invoice.lines.data.reduce((total, line) => total + line.amount, 0);
}
// Good - boundary code maps vendor data into domain data first
function calculateInvoiceTotal(invoice: Invoice): number {
return invoice.lines.reduce((total, line) => total + line.amountCents, 0);
}// Bad - no responsibility, just another place to click through
function getUserName(user: User): string {
return user.name;
}
// Good - the abstraction carries a domain rule
function getDisplayName(user: User): string {
return user.preferredName ?? user.name;
}// Bad - business behavior is mixed with construction and config
async function sendReceipt(order: Order) {
const payments = new StripePayments(process.env.STRIPE_KEY);
const emailer = new SendGridEmailer(process.env.SENDGRID_KEY);
const receipt = await payments.createReceipt(order);
await emailer.send(order.customerEmail, receipt);
}
// Good - construction happens at the edge; behavior uses explicit dependencies
async function sendReceipt(order: Order, payments: Payments, emailer: Emailer) {
const receipt = await payments.createReceipt(order);
await emailer.send(order.customerEmail, receipt);
}init()load()run()// Bad - caller must know the required order
const importer = new Importer();
await importer.connect();
await importer.loadSchema();
await importer.run(file);
// Good - setup returns the usable dependency
const importer = await createImporter(config);
await importer.run(file);// Bad - implementation details become dependencies
export function normalizeLineItem() {}
export function calculateSubtotal() {}
export function applyInvoiceDiscounts() {}
// Good - one intentional public operation
export function calculateInvoiceTotal() {}