TypeScript refactoring: SOLID principles, DRY, type safety improvements, and eliminating code smells
51
55%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./skills/refactoring-typescript/SKILL.mdtsc --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.c0b2e4b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.