refactoring-typescript
TypeScript refactoring: SOLID principles, DRY, type safety improvements, and eliminating code smells
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
TypeScript refactoring: SOLID principles, DRY, type safety improvements, and eliminating code smells
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Architecture decisions: layering, design patterns, microservices trade-offs, and domain-driven design
Python architecture: PEP-8, type hints, clean layering, dependency injection, and testing strategy
General code review process: priority ordering, what to block on, how to give actionable feedback
Python data science: notebook structure, data validation, reproducibility, and model documentation
Git workflow: branch naming, conventional commits, PR conventions, and merge strategies
Security checklist: OWASP top 10, secret scanning, input validation, and auth patterns
| name | refactoring-typescript |
| description | TypeScript refactoring: SOLID principles, DRY, type safety improvements, and eliminating code smells |
tsc --noEmit and eslint after each change.any with proper types or unknown + type guard.strict: true in tsconfig.json if not set.interface for object shapes; type for unions and intersections.readonly arrays and properties when data should not be mutated.// Before
function process(data: any) { ... }
// After
function process(data: unknown) {
if (!isOrderData(data)) throw new TypeError('Invalid order data');
...
}
// Prefer discriminated unions over boolean flags
type Result =
| { status: 'ok'; value: string }
| { status: 'error'; message: string };
is, has, can, should.url, id, ctx).// Before
const d = users.filter(u => u.a);
// After
const activeUsers = users.filter(user => user.isActive);
if/else chains that grow over time.| Smell | Fix |
|---|---|
| Long parameter list (4+) | Extract to typed options object |
| Deep nesting | Early returns / guard clauses |
| Magic numbers/strings | Named constants or enums |
| Copy-pasted logic | Extract to named function or generic utility |
| Large file (300+ lines) | Split into focused modules |
| Boolean parameter | Two functions, or discriminated union |
| Comments explaining what | Rename so it's self-evident |
| Mutable shared state | Encapsulate behind a function; return new values |
| Callback pyramid | Async/await with named steps |
// Before — deep nesting
function processOrder(order: Order) {
if (order) {
if (order.items.length > 0) {
if (order.status === 'pending') {
// actual logic...
}
}
}
}
// After — guard clauses
function processOrder(order: Order) {
if (!order) return;
if (order.items.length === 0) return;
if (order.status !== 'pending') return;
// actual logic...
}
// Before — inline logic that hides intent
const total = items.reduce((sum, item) => {
const discounted = item.price * (1 - item.discountRate);
return sum + discounted * item.quantity;
}, 0);
// After — named helpers reveal intent
const lineTotal = (item: Item) => item.price * (1 - item.discountRate) * item.quantity;
const total = items.reduce((sum, item) => sum + lineTotal(item), 0);
// Before
function createUser(name: string, age: number, role: string, active: boolean) { ... }
// After
interface CreateUserOptions {
name: string;
age: number;
role: string;
active: boolean;
}
function createUser(options: CreateUserOptions) { ... }
// Before — callback pyramid / chained .then
fetchUser(id)
.then(user => fetchOrders(user.id)
.then(orders => fetchInvoices(orders[0].id)
.then(invoices => process(user, orders, invoices))));
// After — sequential async/await
async function loadUserData(id: string) {
const user = await fetchUser(id);
const orders = await fetchOrders(user.id);
const invoices = await fetchInvoices(orders[0].id);
return process(user, orders, invoices);
}
// Before — growing switch that requires editing this function for each new type
function calculateShipping(method: string, weight: number): number {
if (method === 'standard') return weight * 0.5;
if (method === 'express') return weight * 1.2;
if (method === 'overnight') return weight * 2.5;
throw new Error('Unknown method');
}
// After — register new strategies without touching existing code
type ShippingStrategy = (weight: number) => number;
const shippingStrategies: Record<string, ShippingStrategy> = {
standard: w => w * 0.5,
express: w => w * 1.2,
overnight: w => w * 2.5,
};
function calculateShipping(method: string, weight: number): number {
const strategy = shippingStrategies[method];
if (!strategy) throw new Error(`Unknown shipping method: ${method}`);
return strategy(weight);
}
// removed comments.tsc --noEmit and your linter's no-unused-vars / @typescript-eslint/no-unused-vars to surface dead code automatically.