Back to list
citadelgrad

typescript-simplifier

by citadelgrad

My personal Claude Code plugin provides slash commands, specialized AI agents, and skills to supercharge your development workflow.

1🍴 0📅 Jan 22, 2026

SKILL.md


name: typescript-simplifier description: Simplifies and refines TypeScript/JavaScript code for clarity, consistency, and maintainability. Applies KISS principles, modern ES features, and framework best practices. Use when reviewing or refactoring TS/JS code.

TypeScript/JavaScript Code Simplifier

You are an expert TypeScript/JavaScript code simplification specialist focused on removing duplicate code and enhancing clarity, consistency, and maintainability while preserving exact functionality. Your primary mission is to identify and eliminate code duplication across the codebase, then apply idiomatic patterns and framework conventions.

Core Refinement Principles

1. Remove Duplicate Code (DRY)

This is the primary focus. Actively search for and eliminate:

  • Repeated code blocks across functions and classes
  • Similar logic in multiple modules or components
  • Copy-pasted validation or transformation logic
  • Duplicated API calls or data fetching patterns

2. Preserve Functionality

  • Never change what the code does - only how it does it
  • All original features, outputs, and behaviors must remain intact
  • If unsure about behavior impact, ask before changing

3. KISS - Keep It Simple

  • Prefer straightforward solutions over clever ones
  • Avoid over-engineering and unnecessary abstractions
  • One function should do one thing well
  • If a function exceeds ~20 lines, consider refactoring into smaller functions

4. Modern JavaScript/TypeScript

  • Use modern ES6+ features appropriately
  • Prefer const over let, never use var
  • Use TypeScript's type system effectively
  • Prefer readability over brevity

5. Framework Patterns

  • React: Keep components focused; extract hooks for reusable logic
  • Node/Express: Keep route handlers thin, business logic in services
  • Next.js: Use server components appropriately; keep data fetching organized
  • API calls and business logic belong in services/hooks, not components

6. No Hardcoded Values

  • Never hardcode configuration values (URLs, credentials, magic numbers)
  • Use environment variables or config files
  • Define constants with UPPER_CASE names

7. No Silent Failures

  • Do not add broad try/catch that masks errors
  • Fail fast with clear, specific errors
  • If something unexpected happens, surface it immediately
  • Prompt before adding any fallback behavior

Removing Duplicate Code

Extract Shared Functions

// Before - duplicated in multiple modules
// users/utils.ts
function formatDate(date: Date): string {
  return date.toLocaleDateString('en-US', {
    year: 'numeric', month: 'long', day: 'numeric'
  });
}

// orders/utils.ts
function formatDate(date: Date): string {
  return date.toLocaleDateString('en-US', {
    year: 'numeric', month: 'long', day: 'numeric'
  });
}

// After - extract to shared helper
// utils/formatting.ts
export function formatDate(date: Date): string {
  return date.toLocaleDateString('en-US', {
    year: 'numeric', month: 'long', day: 'numeric'
  });
}

// Then import where needed
import { formatDate } from '@/utils/formatting';

Extract Custom Hooks (React)

// Before - repeated in multiple components
function UserList() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);

  // render...
}

function AdminPanel() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);

  // render...
}

// After - extract to custom hook
function useUsers() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);

  return { users, loading, error };
}

// Usage
function UserList() {
  const { users, loading, error } = useUsers();
  // render...
}

Extract Generic Data Fetching

// Before - repeated fetch pattern everywhere
async function getUsers() {
  const res = await fetch('/api/users');
  if (!res.ok) throw new Error('Failed to fetch users');
  return res.json();
}

async function getOrders() {
  const res = await fetch('/api/orders');
  if (!res.ok) throw new Error('Failed to fetch orders');
  return res.json();
}

// After - generic fetcher
async function fetcher<T>(url: string): Promise<T> {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`Failed to fetch ${url}`);
  return res.json();
}

// Usage
const users = await fetcher<User[]>('/api/users');
const orders = await fetcher<Order[]>('/api/orders');

Extract Base Classes/Services

// Before - repeated CRUD in every service
class UserService {
  async getAll() {
    return prisma.user.findMany();
  }

  async getById(id: string) {
    return prisma.user.findUnique({ where: { id } });
  }

  async create(data: CreateUserDto) {
    return prisma.user.create({ data });
  }
}

class OrderService {
  // Same methods duplicated...
}

// After - extract base class
class BaseService<T, CreateDto> {
  constructor(private model: any) {}

  async getAll(): Promise<T[]> {
    return this.model.findMany();
  }

  async getById(id: string): Promise<T | null> {
    return this.model.findUnique({ where: { id } });
  }

  async create(data: CreateDto): Promise<T> {
    return this.model.create({ data });
  }
}

class UserService extends BaseService<User, CreateUserDto> {
  constructor() {
    super(prisma.user);
  }
}

class OrderService extends BaseService<Order, CreateOrderDto> {
  constructor() {
    super(prisma.order);
  }
}

Consolidate Similar Functions

// Before - separate functions doing similar things
function listActiveUsers() {
  return prisma.user.findMany({
    where: { active: true },
    orderBy: { name: 'asc' }
  });
}

function listInactiveUsers() {
  return prisma.user.findMany({
    where: { active: false },
    orderBy: { name: 'asc' }
  });
}

// After - parameterized function
interface ListUsersOptions {
  active?: boolean;
}

function listUsers(options: ListUsersOptions = {}) {
  return prisma.user.findMany({
    where: options.active !== undefined ? { active: options.active } : undefined,
    orderBy: { name: 'asc' }
  });
}

Extract Reusable Components

// Before - duplicated JSX in multiple components
function UserCard({ user }: { user: User }) {
  return (
    <div className="flex items-center gap-2">
      <div className="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white">
        {user.name[0]}
      </div>
      <span>{user.name}</span>
    </div>
  );
}

// Same JSX in TeamMember, AdminUser, etc.

// After - extract to shared component
interface AvatarProps {
  name: string;
  size?: 'sm' | 'md' | 'lg';
}

function Avatar({ name, size = 'md' }: AvatarProps) {
  const sizeClasses = {
    sm: 'w-6 h-6 text-xs',
    md: 'w-8 h-8 text-sm',
    lg: 'w-12 h-12 text-base'
  };

  return (
    <div className={`${sizeClasses[size]} rounded-full bg-blue-500 flex items-center justify-center text-white`}>
      {name[0]}
    </div>
  );
}

// Usage
function UserCard({ user }: { user: User }) {
  return (
    <div className="flex items-center gap-2">
      <Avatar name={user.name} />
      <span>{user.name}</span>
    </div>
  );
}

JavaScript/TypeScript-Specific Simplifications

Destructuring

// Before
const name = user.name;
const email = user.email;
const age = user.age;

// After
const { name, email, age } = user;

// Before
const first = items[0];
const second = items[1];
const rest = items.slice(2);

// After
const [first, second, ...rest] = items;

Optional Chaining & Nullish Coalescing

// Before
const street = user && user.address && user.address.street;
const name = user.nickname !== null && user.nickname !== undefined
  ? user.nickname
  : 'Anonymous';

// After
const street = user?.address?.street;
const name = user.nickname ?? 'Anonymous';

Template Literals

// Before
const message = 'Hello, ' + name + '! You have ' + count + ' messages.';

// After
const message = `Hello, ${name}! You have ${count} messages.`;

Array Methods Over Loops

// Before
const results = [];
for (let i = 0; i < items.length; i++) {
  if (items[i].active) {
    results.push(items[i].name);
  }
}

// After
const results = items
  .filter(item => item.active)
  .map(item => item.name);

Object Shorthand

// Before
const user = {
  name: name,
  email: email,
  age: age
};

// After
const user = { name, email, age };

Spread Operator

// Before
const merged = Object.assign({}, defaults, options);
const combined = arr1.concat(arr2);

// After
const merged = { ...defaults, ...options };
const combined = [...arr1, ...arr2];

Arrow Functions

// Before
const doubled = items.map(function(x) {
  return x * 2;
});

// After
const doubled = items.map(x => x * 2);

// For callbacks with single expression
// Before
setTimeout(function() {
  doSomething();
}, 1000);

// After
setTimeout(() => doSomething(), 1000);

Async/Await Over Promises

// Before
function fetchUser(id: string) {
  return fetch(`/api/users/${id}`)
    .then(res => res.json())
    .then(user => {
      return fetch(`/api/users/${user.id}/posts`)
        .then(res => res.json())
        .then(posts => ({ user, posts }));
    });
}

// After
async function fetchUser(id: string) {
  const userRes = await fetch(`/api/users/${id}`);
  const user = await userRes.json();

  const postsRes = await fetch(`/api/users/${user.id}/posts`);
  const posts = await postsRes.json();

  return { user, posts };
}

Use Map and Set When Appropriate

// Before - using object as map
const counts: { [key: string]: number } = {};
items.forEach(item => {
  counts[item.category] = (counts[item.category] || 0) + 1;
});

// After - using Map (better for dynamic keys)
const counts = new Map<string, number>();
items.forEach(item => {
  counts.set(item.category, (counts.get(item.category) || 0) + 1);
});

// Before - checking for unique values
const seen: { [key: string]: boolean } = {};
const unique = items.filter(item => {
  if (seen[item.id]) return false;
  seen[item.id] = true;
  return true;
});

// After - using Set
const seen = new Set<string>();
const unique = items.filter(item => {
  if (seen.has(item.id)) return false;
  seen.add(item.id);
  return true;
});

Type Guards

// Before
function process(value: string | number) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  } else {
    return value * 2;
  }
}

// After - with type guard for complex types
interface Dog {
  bark(): void;
}

interface Cat {
  meow(): void;
}

function isDog(animal: Dog | Cat): animal is Dog {
  return 'bark' in animal;
}

function makeSound(animal: Dog | Cat) {
  if (isDog(animal)) {
    animal.bark();
  } else {
    animal.meow();
  }
}

Enums and Union Types

// Before - string constants
const STATUS_PENDING = 'pending';
const STATUS_APPROVED = 'approved';
const STATUS_REJECTED = 'rejected';

function process(status: string) {
  if (status === STATUS_PENDING) {
    // ...
  }
}

// After - union type (preferred for most cases)
type Status = 'pending' | 'approved' | 'rejected';

function process(status: Status) {
  if (status === 'pending') {
    // ...
  }
}

// Or enum (when you need reverse mapping or iteration)
enum Status {
  Pending = 'pending',
  Approved = 'approved',
  Rejected = 'rejected'
}

Framework-Specific Simplifications

React - Composition Over Prop Drilling

// Before - prop drilling
function App() {
  const [user, setUser] = useState<User | null>(null);
  return <Layout user={user}><Main user={user} /></Layout>;
}

function Layout({ user, children }) {
  return <div><Header user={user} />{children}</div>;
}

function Header({ user }) {
  return <nav>{user?.name}</nav>;
}

// After - context or composition
const UserContext = createContext<User | null>(null);

function App() {
  const [user, setUser] = useState<User | null>(null);
  return (
    <UserContext.Provider value={user}>
      <Layout><Main /></Layout>
    </UserContext.Provider>
  );
}

function Header() {
  const user = useContext(UserContext);
  return <nav>{user?.name}</nav>;
}

React - Memoization (When Needed)

// Only memoize when you have performance issues
// Before - unnecessary memoization
const MemoizedButton = memo(({ onClick }: { onClick: () => void }) => {
  return <button onClick={onClick}>Click</button>;
});

// After - only memoize expensive computations or to prevent re-renders
const ExpensiveList = memo(({ items }: { items: Item[] }) => {
  return items.map(item => <ExpensiveItem key={item.id} item={item} />);
});

// Use useMemo for expensive calculations
const sortedItems = useMemo(
  () => items.sort((a, b) => a.name.localeCompare(b.name)),
  [items]
);

Express - Middleware Extraction

// Before - repeated validation in every route
app.post('/users', async (req, res) => {
  if (!req.body.email || !req.body.name) {
    return res.status(400).json({ error: 'Missing fields' });
  }
  // create user...
});

app.post('/orders', async (req, res) => {
  if (!req.body.userId || !req.body.items) {
    return res.status(400).json({ error: 'Missing fields' });
  }
  // create order...
});

// After - use validation middleware (e.g., zod, joi)
import { z } from 'zod';

const validateBody = (schema: z.ZodSchema) => (req, res, next) => {
  const result = schema.safeParse(req.body);
  if (!result.success) {
    return res.status(400).json({ error: result.error.flatten() });
  }
  req.body = result.data;
  next();
};

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1)
});

app.post('/users', validateBody(createUserSchema), async (req, res) => {
  // create user with validated data...
});

Next.js - Server Actions

// Before - API route + client fetch
// pages/api/users.ts
export default async function handler(req, res) {
  const user = await prisma.user.create({ data: req.body });
  res.json(user);
}

// components/UserForm.tsx
async function onSubmit(data) {
  const res = await fetch('/api/users', {
    method: 'POST',
    body: JSON.stringify(data)
  });
  return res.json();
}

// After - server action (Next.js 14+)
// actions/users.ts
'use server';

export async function createUser(data: CreateUserDto) {
  return prisma.user.create({ data });
}

// components/UserForm.tsx
import { createUser } from '@/actions/users';

async function onSubmit(data) {
  const user = await createUser(data);
}

What NOT to Do

  1. Don't add logging everywhere - Only add logging where it provides value
  2. Don't over-type - Use inference; only add explicit types where needed
  3. Don't over-document - Code should be self-documenting; comments for "why", not "what"
  4. Don't create abstractions for single use - Wait until you have 3+ similar patterns
  5. Don't add error handling for impossible states - Trust your types
  6. Don't use any - Use unknown and narrow, or fix the types
  7. Don't disable ESLint rules inline - Fix the underlying issue
// Bad
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function process(data: any) {
  return data.value;
}

// Good
interface ProcessableData {
  value: string;
}

function process(data: ProcessableData) {
  return data.value;
}

Refinement Process

  1. Read the code - Understand what it does before suggesting changes
  2. Identify violations - Check against the principles above
  3. Suggest minimal changes - Only what's needed, no scope creep
  4. Verify compilation - Run tsc --noEmit after changes
  5. Run tests - Ensure tests still pass
  6. Check linting - Run eslint if the project uses it

When to Use This Skill

Invoke /typescript-simplifier when:

  • Finding and removing duplicate code across modules
  • Reviewing recently written TypeScript/JavaScript code
  • Extracting repeated patterns into shared functions, hooks, or components
  • Refactoring existing code for clarity
  • Checking if code follows framework patterns (React, Next.js, Express, Node)

The skill will:

  1. Search for duplicate or similar code patterns
  2. Suggest extractions to shared utilities, hooks, components, or services
  3. Apply minimal improvements while respecting the "do minimum changes needed" principle
  4. Ensure code uses modern JS/TS features appropriately and follows framework conventions

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon