Use when writing, fixing, editing, or reviewing TypeScript code quality. Enforces Clean Code's core principles—DRY, single responsibility, clear intent, no magic numbers, proper abstractions. Also trigger on: duplicated logic across files or branches (G5), magic numbers or hardcoded strings (G25), long if/else chains that should be union types plus polymorphism (G23), chained property access like `a.b.c.d` or long optional-chain trains (G36), functions juggling multiple responsibilities (G30)...
Scanned 9/19/2026
npx -y skills add CasLubbers/code-design-skills --skill ts-clean-general --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ts Clean General?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/caslubbers-ts-clean-general)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: ts-clean-general
description: Use when writing, fixing, editing, or reviewing TypeScript code quality. Enforces Clean Code's core principles—DRY, single responsibility, clear intent, no magic numbers, proper abstractions. Also trigger on: duplicated logic across files or branches (G5), magic numbers or hardcoded strings (G25), long if/else chains that should be union types plus polymorphism (G23), chained property access like `a.b.c.d` or long optional-chain trains (G36), functions juggling multiple responsibilities (G30), clever one-liners whose intent is not obvious (G16). Also trigger on: a class extending another to reuse one method, an override that throws, a hierarchy three levels deep, an abstract class standing in for a union type, or asks like "should this extend or compose", "composition over inheritance".
---
# General Clean Code Principles
## Critical Rules
**G5: DRY (Don't Repeat Yourself)**
Every piece of knowledge has one authoritative representation.
```ts
// Bad - duplication
const taxRate = 0.0825;
const caTotal = subtotal * 1.0825;
const nyTotal = subtotal * 1.07;
// Good - single source of truth
const TAX_RATES: Record<string, number> = { CA: 0.0825, NY: 0.07 };
function calculateTotal(subtotal: number, state: string): number {
return subtotal * (1 + TAX_RATES[state]);
}
```
**G16: No Obscured Intent**
Don't be clever. Be clear.
```ts
// Bad - what does this do?
return ((x & 0x0f) << 4) | (y & 0x0f);
// Good - obvious intent
return packCoordinates(x, y);
```
**G23: Prefer Polymorphism to If/Else**
```ts
// Bad - will grow forever
function calculatePay(employee: {
type: "SALARIED" | "HOURLY" | "COMMISSIONED";
salary?: number;
hours?: number;
rate?: number;
base?: number;
commission?: number;
}): number {
if (employee.type === "SALARIED") {
return employee.salary ?? 0;
} else if (employee.type === "HOURLY") {
return (employee.hours ?? 0) * (employee.rate ?? 0);
} else if (employee.type === "COMMISSIONED") {
return (employee.base ?? 0) + (employee.commission ?? 0);
}
return 0;
}
// Good - open/closed principle
interface Employee {
calculatePay(): number;
}
class SalariedEmployee implements Employee {
constructor(private readonly salary: number) {}
calculatePay(): number {
return this.salary;
}
}
class HourlyEmployee implements Employee {
constructor(
private readonly hours: number,
private readonly rate: number,
) {}
calculatePay(): number {
return this.hours * this.rate;
}
}
class CommissionedEmployee implements Employee {
constructor(
private readonly base: number,
private readonly commission: number,
) {}
calculatePay(): number {
return this.base + this.commission;
}
}
```
**G25: Replace Magic Numbers with Named Constants**
```ts
// Bad
if (elapsedTime > 86400) {
// ...
}
// Good
const SECONDS_PER_DAY = 86400;
if (elapsedTime > SECONDS_PER_DAY) {
// ...
}
```
**G30: Functions Should Do One Thing**
If you can extract another function, your function does more than one thing.
**G36: Law of Demeter (Avoid Train Wrecks)**
```ts
// Bad - reaching through multiple objects
const outputDir = context.options.scratchDir.absolutePath;
// Good - one dot
const outputDir = context.getScratchDir();
```
**Composition Over Inheritance**
Inheritance couples a subclass to its parent's internals forever. Use it only for a genuine
is-a relationship where the subclass is substitutable everywhere the parent is. For reuse, compose.
```typescript
// Bad - extends to borrow one method, inherits the entire surface
class EmailNotifier extends SmtpClient {
notify(user: User, message: string) {
this.send(user.email, message)
}
}
// Good - holds what it needs
class EmailNotifier {
constructor(private readonly smtp: SmtpClient) {}
notify(user: User, message: string) {
this.smtp.send(user.email, message)
}
}
```
The composed version takes a fake `SmtpClient` in tests, can swap transports, and exposes only
`notify`. The extended version makes every `SmtpClient` method part of its own public API.
Signals you extended for the wrong reason: an override that throws, an override ignoring parameters
the base requires, or a three-deep hierarchy with the real behaviour in the middle.
TypeScript makes the alternatives cheap. Use an `interface` for the contract, and union types where
subclasses were only ever acting as an enum:
```typescript
// Bad - a class hierarchy standing in for three shapes
abstract class Shape { abstract area(): number }
// Good - discriminated union, exhaustively checkable
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number }
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2
case "square": return shape.side ** 2
}
}
```
In React the same rule reads: compose components and extract hooks. There is no component
inheritance worth having.
## Enforcement Checklist
When reviewing AI-generated code, verify:
- [ ] No duplication (G5)
- [ ] Clear intent, no magic numbers (G16, G25)
- [ ] Polymorphism over conditionals (G23)
- [ ] Functions do one thing (G30)
- [ ] No Law of Demeter violations (G36)
- [ ] Boundary conditions handled (G3)
- [ ] Dead code removed (G9)
- [ ] Composition preferred over inheritance
- [ ] Module reads top-down, callers above callees
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!